From 6222149487d7e241ad7b009dae1f2fa6f69adc5d Mon Sep 17 00:00:00 2001 From: Igor Borovkov Date: Tue, 27 Oct 2009 13:16:44 +0200 Subject: IM refactoring: moved voice channel related classes to its own llvoicechannel.* files from dying llimpanel.* files --HG-- branch : product-engine --- indra/newview/llimpanel.cpp | 830 +------------------------------------------- 1 file changed, 4 insertions(+), 826 deletions(-) (limited to 'indra/newview/llimpanel.cpp') diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 163984f740..2d8372db04 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -77,6 +77,7 @@ #include "llviewercontrol.h" #include "lluictrlfactory.h" #include "llviewerwindow.h" +#include "llvoicechannel.h" #include "lllogchat.h" #include "llweb.h" #include "llhttpclient.h" @@ -90,7 +91,6 @@ const S32 LINE_HEIGHT = 16; const S32 MIN_WIDTH = 200; const S32 MIN_HEIGHT = 130; -const U32 DEFAULT_RETRIES_COUNT = 3; // // Statics @@ -100,831 +100,6 @@ static std::string sTitleString = "Instant Message with [NAME]"; static std::string sTypingStartString = "[NAME]: ..."; static std::string sSessionStartString = "Starting session with [NAME] please wait."; -LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap; -LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap; -LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL; -LLVoiceChannel* LLVoiceChannel::sSuspendedVoiceChannel = NULL; - -BOOL LLVoiceChannel::sSuspended = FALSE; - - - -class LLVoiceCallCapResponder : public LLHTTPClient::Responder -{ -public: - LLVoiceCallCapResponder(const LLUUID& session_id) : mSessionID(session_id) {}; - - virtual void error(U32 status, const std::string& reason); // called with bad status codes - virtual void result(const LLSD& content); - -private: - LLUUID mSessionID; -}; - - -void LLVoiceCallCapResponder::error(U32 status, const std::string& reason) -{ - llwarns << "LLVoiceCallCapResponder::error(" - << status << ": " << reason << ")" - << llendl; - LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); - if ( channelp ) - { - if ( 403 == status ) - { - //403 == no ability - LLNotifications::instance().add( - "VoiceNotAllowed", - channelp->getNotifyArgs()); - } - else - { - LLNotifications::instance().add( - "VoiceCallGenericError", - channelp->getNotifyArgs()); - } - channelp->deactivate(); - } -} - -void LLVoiceCallCapResponder::result(const LLSD& content) -{ - LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); - if (channelp) - { - //*TODO: DEBUG SPAM - LLSD::map_const_iterator iter; - for(iter = content.beginMap(); iter != content.endMap(); ++iter) - { - llinfos << "LLVoiceCallCapResponder::result got " - << iter->first << llendl; - } - - channelp->setChannelInfo( - content["voice_credentials"]["channel_uri"].asString(), - content["voice_credentials"]["channel_credentials"].asString()); - } -} - -// -// LLVoiceChannel -// -LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const std::string& session_name) : - mSessionID(session_id), - mState(STATE_NO_CHANNEL_INFO), - mSessionName(session_name), - mIgnoreNextSessionLeave(FALSE) -{ - mNotifyArgs["VOICE_CHANNEL_NAME"] = mSessionName; - - if (!sVoiceChannelMap.insert(std::make_pair(session_id, this)).second) - { - // a voice channel already exists for this session id, so this instance will be orphaned - // the end result should simply be the failure to make voice calls - llwarns << "Duplicate voice channels registered for session_id " << session_id << llendl; - } - - LLVoiceClient::getInstance()->addObserver(this); -} - -LLVoiceChannel::~LLVoiceChannel() -{ - // Don't use LLVoiceClient::getInstance() here -- this can get called during atexit() time and that singleton MAY have already been destroyed. - if(gVoiceClient) - { - gVoiceClient->removeObserver(this); - } - - sVoiceChannelMap.erase(mSessionID); - sVoiceChannelURIMap.erase(mURI); -} - -void LLVoiceChannel::setChannelInfo( - const std::string& uri, - const std::string& credentials) -{ - setURI(uri); - - mCredentials = credentials; - - if (mState == STATE_NO_CHANNEL_INFO) - { - if (mURI.empty()) - { - LLNotifications::instance().add("VoiceChannelJoinFailed", mNotifyArgs); - llwarns << "Received empty URI for channel " << mSessionName << llendl; - deactivate(); - } - else if (mCredentials.empty()) - { - LLNotifications::instance().add("VoiceChannelJoinFailed", mNotifyArgs); - llwarns << "Received empty credentials for channel " << mSessionName << llendl; - deactivate(); - } - else - { - setState(STATE_READY); - - // if we are supposed to be active, reconnect - // this will happen on initial connect, as we request credentials on first use - if (sCurrentVoiceChannel == this) - { - // just in case we got new channel info while active - // should move over to new channel - activate(); - } - } - } -} - -void LLVoiceChannel::onChange(EStatusType type, const std::string &channelURI, bool proximal) -{ - if (channelURI != mURI) - { - return; - } - - if (type < BEGIN_ERROR_STATUS) - { - handleStatusChange(type); - } - else - { - handleError(type); - } -} - -void LLVoiceChannel::handleStatusChange(EStatusType type) -{ - // status updates - switch(type) - { - case STATUS_LOGIN_RETRY: - //mLoginNotificationHandle = LLNotifyBox::showXml("VoiceLoginRetry")->getHandle(); - LLNotifications::instance().add("VoiceLoginRetry"); - break; - case STATUS_LOGGED_IN: - //if (!mLoginNotificationHandle.isDead()) - //{ - // LLNotifyBox* notifyp = (LLNotifyBox*)mLoginNotificationHandle.get(); - // if (notifyp) - // { - // notifyp->close(); - // } - // mLoginNotificationHandle.markDead(); - //} - break; - case STATUS_LEFT_CHANNEL: - if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) - { - // if forceably removed from channel - // update the UI and revert to default channel - LLNotifications::instance().add("VoiceChannelDisconnected", mNotifyArgs); - deactivate(); - } - mIgnoreNextSessionLeave = FALSE; - break; - case STATUS_JOINING: - if (callStarted()) - { - setState(STATE_RINGING); - } - break; - case STATUS_JOINED: - if (callStarted()) - { - setState(STATE_CONNECTED); - } - default: - break; - } -} - -// default behavior is to just deactivate channel -// derived classes provide specific error messages -void LLVoiceChannel::handleError(EStatusType type) -{ - deactivate(); - setState(STATE_ERROR); -} - -BOOL LLVoiceChannel::isActive() -{ - // only considered active when currently bound channel matches what our channel - return callStarted() && LLVoiceClient::getInstance()->getCurrentChannel() == mURI; -} - -BOOL LLVoiceChannel::callStarted() -{ - return mState >= STATE_CALL_STARTED; -} - -void LLVoiceChannel::deactivate() -{ - if (mState >= STATE_RINGING) - { - // ignore session leave event - mIgnoreNextSessionLeave = TRUE; - } - - if (callStarted()) - { - setState(STATE_HUNG_UP); - // mute the microphone if required when returning to the proximal channel - if (gSavedSettings.getBOOL("AutoDisengageMic") && sCurrentVoiceChannel == this) - { - gSavedSettings.setBOOL("PTTCurrentlyEnabled", true); - } - } - - if (sCurrentVoiceChannel == this) - { - // default channel is proximal channel - sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); - sCurrentVoiceChannel->activate(); - } -} - -void LLVoiceChannel::activate() -{ - if (callStarted()) - { - return; - } - - // deactivate old channel and mark ourselves as the active one - if (sCurrentVoiceChannel != this) - { - // mark as current before deactivating the old channel to prevent - // activating the proximal channel between IM calls - LLVoiceChannel* old_channel = sCurrentVoiceChannel; - sCurrentVoiceChannel = this; - if (old_channel) - { - old_channel->deactivate(); - } - } - - if (mState == STATE_NO_CHANNEL_INFO) - { - // responsible for setting status to active - getChannelInfo(); - } - else - { - setState(STATE_CALL_STARTED); - } -} - -void LLVoiceChannel::getChannelInfo() -{ - // pretend we have everything we need - if (sCurrentVoiceChannel == this) - { - setState(STATE_CALL_STARTED); - } -} - -//static -LLVoiceChannel* LLVoiceChannel::getChannelByID(const LLUUID& session_id) -{ - voice_channel_map_t::iterator found_it = sVoiceChannelMap.find(session_id); - if (found_it == sVoiceChannelMap.end()) - { - return NULL; - } - else - { - return found_it->second; - } -} - -//static -LLVoiceChannel* LLVoiceChannel::getChannelByURI(std::string uri) -{ - voice_channel_map_uri_t::iterator found_it = sVoiceChannelURIMap.find(uri); - if (found_it == sVoiceChannelURIMap.end()) - { - return NULL; - } - else - { - return found_it->second; - } -} - -void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id) -{ - sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID)); - mSessionID = new_session_id; - sVoiceChannelMap.insert(std::make_pair(mSessionID, this)); -} - -void LLVoiceChannel::setURI(std::string uri) -{ - sVoiceChannelURIMap.erase(mURI); - mURI = uri; - sVoiceChannelURIMap.insert(std::make_pair(mURI, this)); -} - -void LLVoiceChannel::setState(EState state) -{ - switch(state) - { - case STATE_RINGING: - gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs); - break; - case STATE_CONNECTED: - gIMMgr->addSystemMessage(mSessionID, "connected", mNotifyArgs); - break; - case STATE_HUNG_UP: - gIMMgr->addSystemMessage(mSessionID, "hang_up", mNotifyArgs); - break; - default: - break; - } - - mState = state; -} - -void LLVoiceChannel::toggleCallWindowIfNeeded(EState state) -{ - if (state == STATE_CONNECTED) - { - LLFloaterReg::showInstance("voice_call", mSessionID); - } - // By checking that current state is CONNECTED we make sure that the call window - // has been shown, hence there's something to hide. This helps when user presses - // the "End call" button right after initiating the call. - // *TODO: move this check to LLFloaterCall? - else if (state == STATE_HUNG_UP && mState == STATE_CONNECTED) - { - LLFloaterReg::hideInstance("voice_call", mSessionID); - } -} - -//static -void LLVoiceChannel::initClass() -{ - sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); -} - - -//static -void LLVoiceChannel::suspend() -{ - if (!sSuspended) - { - sSuspendedVoiceChannel = sCurrentVoiceChannel; - sSuspended = TRUE; - } -} - -//static -void LLVoiceChannel::resume() -{ - if (sSuspended) - { - if (gVoiceClient->voiceEnabled()) - { - if (sSuspendedVoiceChannel) - { - sSuspendedVoiceChannel->activate(); - } - else - { - LLVoiceChannelProximal::getInstance()->activate(); - } - } - sSuspended = FALSE; - } -} - - -// -// LLVoiceChannelGroup -// - -LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name) : - LLVoiceChannel(session_id, session_name) -{ - mRetries = DEFAULT_RETRIES_COUNT; - mIsRetrying = FALSE; -} - -void LLVoiceChannelGroup::deactivate() -{ - if (callStarted()) - { - LLVoiceClient::getInstance()->leaveNonSpatialChannel(); - } - LLVoiceChannel::deactivate(); -} - -void LLVoiceChannelGroup::activate() -{ - if (callStarted()) return; - - LLVoiceChannel::activate(); - - if (callStarted()) - { - // we have the channel info, just need to use it now - LLVoiceClient::getInstance()->setNonSpatialChannel( - mURI, - mCredentials); - -#if 0 // *TODO - if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel - { - // Add the party to the list of people with which we've recently interacted. - for (/*people in the chat*/) - LLRecentPeople::instance().add(buddy_id); - } -#endif - } -} - -void LLVoiceChannelGroup::getChannelInfo() -{ - LLViewerRegion* region = gAgent.getRegion(); - if (region) - { - std::string url = region->getCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "call"; - data["session-id"] = mSessionID; - LLHTTPClient::post(url, - data, - new LLVoiceCallCapResponder(mSessionID)); - } -} - -void LLVoiceChannelGroup::setChannelInfo( - const std::string& uri, - const std::string& credentials) -{ - setURI(uri); - - mCredentials = credentials; - - if (mState == STATE_NO_CHANNEL_INFO) - { - if(!mURI.empty() && !mCredentials.empty()) - { - setState(STATE_READY); - - // if we are supposed to be active, reconnect - // this will happen on initial connect, as we request credentials on first use - if (sCurrentVoiceChannel == this) - { - // just in case we got new channel info while active - // should move over to new channel - activate(); - } - } - else - { - //*TODO: notify user - llwarns << "Received invalid credentials for channel " << mSessionName << llendl; - deactivate(); - } - } - else if ( mIsRetrying ) - { - // we have the channel info, just need to use it now - LLVoiceClient::getInstance()->setNonSpatialChannel( - mURI, - mCredentials); - } -} - -void LLVoiceChannelGroup::handleStatusChange(EStatusType type) -{ - // status updates - switch(type) - { - case STATUS_JOINED: - mRetries = 3; - mIsRetrying = FALSE; - default: - break; - } - - LLVoiceChannel::handleStatusChange(type); -} - -void LLVoiceChannelGroup::handleError(EStatusType status) -{ - std::string notify; - switch(status) - { - case ERROR_CHANNEL_LOCKED: - case ERROR_CHANNEL_FULL: - notify = "VoiceChannelFull"; - break; - case ERROR_NOT_AVAILABLE: - //clear URI and credentials - //set the state to be no info - //and activate - if ( mRetries > 0 ) - { - mRetries--; - mIsRetrying = TRUE; - mIgnoreNextSessionLeave = TRUE; - - getChannelInfo(); - return; - } - else - { - notify = "VoiceChannelJoinFailed"; - mRetries = DEFAULT_RETRIES_COUNT; - mIsRetrying = FALSE; - } - - break; - - case ERROR_UNKNOWN: - default: - break; - } - - // notification - if (!notify.empty()) - { - LLNotificationPtr notification = LLNotifications::instance().add(notify, mNotifyArgs); - // echo to im window - gIMMgr->addMessage(mSessionID, LLUUID::null, SYSTEM_FROM, notification->getMessage()); - } - - LLVoiceChannel::handleError(status); -} - -void LLVoiceChannelGroup::setState(EState state) -{ - // HACK: Open/close the call window if needed. - toggleCallWindowIfNeeded(state); - - switch(state) - { - case STATE_RINGING: - if ( !mIsRetrying ) - { - gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs); - } - - mState = state; - break; - default: - LLVoiceChannel::setState(state); - } -} - -// -// LLVoiceChannelProximal -// -LLVoiceChannelProximal::LLVoiceChannelProximal() : - LLVoiceChannel(LLUUID::null, LLStringUtil::null) -{ - activate(); -} - -BOOL LLVoiceChannelProximal::isActive() -{ - return callStarted() && LLVoiceClient::getInstance()->inProximalChannel(); -} - -void LLVoiceChannelProximal::activate() -{ - if (callStarted()) return; - - LLVoiceChannel::activate(); - - if (callStarted()) - { - // this implicitly puts you back in the spatial channel - LLVoiceClient::getInstance()->leaveNonSpatialChannel(); - } -} - -void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal) -{ - if (!proximal) - { - return; - } - - if (type < BEGIN_ERROR_STATUS) - { - handleStatusChange(type); - } - else - { - handleError(type); - } -} - -void LLVoiceChannelProximal::handleStatusChange(EStatusType status) -{ - // status updates - switch(status) - { - case STATUS_LEFT_CHANNEL: - // do not notify user when leaving proximal channel - return; - case STATUS_VOICE_DISABLED: - gIMMgr->addSystemMessage(LLUUID::null, "unavailable", mNotifyArgs); - return; - default: - break; - } - LLVoiceChannel::handleStatusChange(status); -} - - -void LLVoiceChannelProximal::handleError(EStatusType status) -{ - std::string notify; - switch(status) - { - case ERROR_CHANNEL_LOCKED: - case ERROR_CHANNEL_FULL: - notify = "ProximalVoiceChannelFull"; - break; - default: - break; - } - - // notification - if (!notify.empty()) - { - LLNotifications::instance().add(notify, mNotifyArgs); - } - - LLVoiceChannel::handleError(status); -} - -void LLVoiceChannelProximal::deactivate() -{ - if (callStarted()) - { - setState(STATE_HUNG_UP); - } -} - - -// -// LLVoiceChannelP2P -// -LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id) : - LLVoiceChannelGroup(session_id, session_name), - mOtherUserID(other_user_id), - mReceivedCall(FALSE) -{ - // make sure URI reflects encoded version of other user's agent id - setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id)); -} - -void LLVoiceChannelP2P::handleStatusChange(EStatusType type) -{ - // status updates - switch(type) - { - case STATUS_LEFT_CHANNEL: - if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) - { - if (mState == STATE_RINGING) - { - // other user declined call - LLNotifications::instance().add("P2PCallDeclined", mNotifyArgs); - } - else - { - // other user hung up - LLNotifications::instance().add("VoiceChannelDisconnectedP2P", mNotifyArgs); - } - deactivate(); - } - mIgnoreNextSessionLeave = FALSE; - return; - default: - break; - } - - LLVoiceChannel::handleStatusChange(type); -} - -void LLVoiceChannelP2P::handleError(EStatusType type) -{ - switch(type) - { - case ERROR_NOT_AVAILABLE: - LLNotifications::instance().add("P2PCallNoAnswer", mNotifyArgs); - break; - default: - break; - } - - LLVoiceChannel::handleError(type); -} - -void LLVoiceChannelP2P::activate() -{ - if (callStarted()) return; - - LLVoiceChannel::activate(); - - if (callStarted()) - { - // no session handle yet, we're starting the call - if (mSessionHandle.empty()) - { - mReceivedCall = FALSE; - LLVoiceClient::getInstance()->callUser(mOtherUserID); - } - // otherwise answering the call - else - { - LLVoiceClient::getInstance()->answerInvite(mSessionHandle); - - // using the session handle invalidates it. Clear it out here so we can't reuse it by accident. - mSessionHandle.clear(); - } - - // Add the party to the list of people with which we've recently interacted. - LLRecentPeople::instance().add(mOtherUserID); - } -} - -void LLVoiceChannelP2P::getChannelInfo() -{ - // pretend we have everything we need, since P2P doesn't use channel info - if (sCurrentVoiceChannel == this) - { - setState(STATE_CALL_STARTED); - } -} - -// receiving session from other user who initiated call -void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::string &inURI) -{ - BOOL needs_activate = FALSE; - if (callStarted()) - { - // defer to lower agent id when already active - if (mOtherUserID < gAgent.getID()) - { - // pretend we haven't started the call yet, so we can connect to this session instead - deactivate(); - needs_activate = TRUE; - } - else - { - // we are active and have priority, invite the other user again - // under the assumption they will join this new session - mSessionHandle.clear(); - LLVoiceClient::getInstance()->callUser(mOtherUserID); - return; - } - } - - mSessionHandle = handle; - - // The URI of a p2p session should always be the other end's SIP URI. - if(!inURI.empty()) - { - setURI(inURI); - } - else - { - setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID)); - } - - mReceivedCall = TRUE; - - if (needs_activate) - { - activate(); - } -} - -void LLVoiceChannelP2P::setState(EState state) -{ - // HACK: Open/close the call window if needed. - toggleCallWindowIfNeeded(state); - - // you only "answer" voice invites in p2p mode - // so provide a special purpose message here - if (mReceivedCall && state == STATE_RINGING) - { - gIMMgr->addSystemMessage(mSessionID, "answering", mNotifyArgs); - mState = state; - return; - } - LLVoiceChannel::setState(state); -} - // // LLFloaterIMPanel @@ -1905,6 +1080,9 @@ bool LLFloaterIMPanel::onConfirmForceCloseError(const LLSD& notification, const session_id); if ( floaterp ) floaterp->closeFloater(FALSE); + + + } return false; } -- cgit v1.3 From bfc9445563c5ad200ed609bc8ff3edecb2398d28 Mon Sep 17 00:00:00 2001 From: Eugene Mutavchi Date: Tue, 27 Oct 2009 14:29:18 +0200 Subject: Implemented major task EXT-1717 (IM related server responses processing stuff should use new IM floater along with the old one) sub-task of EXT-96 (Viewer 2009: Group IM detail). --HG-- branch : product-engine --- indra/newview/llimpanel.cpp | 70 --------------------- indra/newview/llimpanel.h | 9 --- indra/newview/llimview.cpp | 149 +++++++++++++++++++++++++++++++++----------- indra/newview/llimview.h | 6 ++ 4 files changed, 117 insertions(+), 117 deletions(-) (limited to 'indra/newview/llimpanel.cpp') diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 2d8372db04..211e657a76 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -1011,78 +1011,8 @@ void LLFloaterIMPanel::chatFromLogFile(LLLogChat::ELogLineType type, std::string self->mHistoryEditor->blockUndo(); } -void LLFloaterIMPanel::showSessionStartError( - const std::string& error_string) -{ - LLSD args; - args["REASON"] = LLTrans::getString(error_string); - args["RECIPIENT"] = getTitle(); - - LLSD payload; - payload["session_id"] = mSessionUUID; - - LLNotifications::instance().add( - "ChatterBoxSessionStartError", - args, - payload, - onConfirmForceCloseError); -} - -void LLFloaterIMPanel::showSessionEventError( - const std::string& event_string, - const std::string& error_string) -{ - LLSD args; - args["REASON"] = - LLTrans::getString(error_string); - args["EVENT"] = - LLTrans::getString(event_string); - args["RECIPIENT"] = getTitle(); - - LLNotifications::instance().add( - "ChatterBoxSessionEventError", - args); -} - -void LLFloaterIMPanel::showSessionForceClose( - const std::string& reason_string) -{ - LLSD args; - - args["NAME"] = getTitle(); - args["REASON"] = LLTrans::getString(reason_string); - - LLSD payload; - payload["session_id"] = mSessionUUID; - - LLNotifications::instance().add( - "ForceCloseChatterBoxSession", - args, - payload, - LLFloaterIMPanel::onConfirmForceCloseError); - -} - //static void LLFloaterIMPanel::onKickSpeaker(void* user_data) { } - -bool LLFloaterIMPanel::onConfirmForceCloseError(const LLSD& notification, const LLSD& response) -{ - //only 1 option really - LLUUID session_id = notification["payload"]["session_id"]; - - if ( gIMMgr ) - { - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession( - session_id); - - if ( floaterp ) floaterp->closeFloater(FALSE); - - - - } - return false; -} diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index 31b5c5c127..39107d9a22 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -129,15 +129,6 @@ public: void processIMTyping(const LLIMInfo* im_info, BOOL typing); static void chatFromLogFile(LLLogChat::ELogLineType type, std::string line, void* userdata); - //show error statuses to the user - void showSessionStartError(const std::string& error_string); - void showSessionEventError( - const std::string& event_string, - const std::string& error_string); - void showSessionForceClose(const std::string& reason); - - static bool onConfirmForceCloseError(const LLSD& notification, const LLSD& response); - private: // Called by UI methods. void sendMsg(); diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 164da4136f..91891fd98e 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -884,20 +884,11 @@ public: { gIMMgr->clearPendingAgentListUpdates(mSessionID); gIMMgr->clearPendingInvitation(mSessionID); - - LLFloaterIMPanel* floaterp = - gIMMgr->findFloaterBySession(mSessionID); - - if ( floaterp ) + if ( 404 == statusNum ) { - if ( 404 == statusNum ) - { - std::string error_string; - error_string = "does not exist"; - - floaterp->showSessionStartError( - error_string); - } + std::string error_string; + error_string = "does not exist"; + gIMMgr->showSessionStartError(error_string, mSessionID); } } } @@ -949,6 +940,106 @@ LLUUID LLIMMgr::computeSessionID( return session_id; } +inline LLFloater* getFloaterBySessionID(const LLUUID session_id) +{ + LLFloater* floater = NULL; + if ( gIMMgr ) + { + floater = dynamic_cast < LLFloater* > + ( gIMMgr->findFloaterBySession(session_id) ); + } + if ( !floater ) + { + floater = dynamic_cast < LLFloater* > + ( LLIMFloater::findInstance(session_id) ); + } + return floater; +} + +void +LLIMMgr::showSessionStartError( + const std::string& error_string, + const LLUUID session_id) +{ + const LLFloater* floater = getFloaterBySessionID (session_id); + if (!floater) return; + + LLSD args; + args["REASON"] = LLTrans::getString(error_string); + args["RECIPIENT"] = floater->getTitle(); + + LLSD payload; + payload["session_id"] = session_id; + + LLNotifications::instance().add( + "ChatterBoxSessionStartError", + args, + payload, + LLIMMgr::onConfirmForceCloseError); +} + +void +LLIMMgr::showSessionEventError( + const std::string& event_string, + const std::string& error_string, + const LLUUID session_id) +{ + const LLFloater* floater = getFloaterBySessionID (session_id); + if (!floater) return; + + LLSD args; + args["REASON"] = + LLTrans::getString(error_string); + args["EVENT"] = + LLTrans::getString(event_string); + args["RECIPIENT"] = floater->getTitle(); + + LLNotifications::instance().add( + "ChatterBoxSessionEventError", + args); +} + +void +LLIMMgr::showSessionForceClose( + const std::string& reason_string, + const LLUUID session_id) +{ + const LLFloater* floater = getFloaterBySessionID (session_id); + if (!floater) return; + + LLSD args; + + args["NAME"] = floater->getTitle(); + args["REASON"] = LLTrans::getString(reason_string); + + LLSD payload; + payload["session_id"] = session_id; + + LLNotifications::instance().add( + "ForceCloseChatterBoxSession", + args, + payload, + LLIMMgr::onConfirmForceCloseError); +} + +//static +bool +LLIMMgr::onConfirmForceCloseError( + const LLSD& notification, + const LLSD& response) +{ + //only 1 option really + LLUUID session_id = notification["payload"]["session_id"]; + + LLFloater* floater = getFloaterBySessionID (session_id); + if ( floater ) + { + floater->closeFloater(FALSE); + } + return false; +} + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLIncomingCallDialog //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2027,15 +2118,8 @@ public: } else { - //throw an error dialog and close the temp session's - //floater - LLFloaterIMPanel* floater = - gIMMgr->findFloaterBySession(temp_session_id); - - if ( floater ) - { - floater->showSessionStartError(body["error"].asString()); - } + //throw an error dialog and close the temp session's floater + gIMMgr->showSessionStartError(body["error"].asString(), temp_session_id); } gIMMgr->clearPendingAgentListUpdates(session_id); @@ -2068,15 +2152,10 @@ public: if ( !success ) { //throw an error dialog - LLFloaterIMPanel* floater = - gIMMgr->findFloaterBySession(session_id); - - if (floater) - { - floater->showSessionEventError( - body["event"].asString(), - body["error"].asString()); - } + gIMMgr->showSessionEventError( + body["event"].asString(), + body["error"].asString(), + session_id); } } }; @@ -2094,13 +2173,7 @@ public: session_id = input["body"]["session_id"].asUUID(); reason = input["body"]["reason"].asString(); - LLFloaterIMPanel* floater = - gIMMgr ->findFloaterBySession(session_id); - - if ( floater ) - { - floater->showSessionForceClose(reason); - } + gIMMgr->showSessionForceClose(reason, session_id); } }; diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index e3d0a50557..87ec429195 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -314,6 +314,12 @@ public: void addSessionObserver(LLIMSessionObserver *); void removeSessionObserver(LLIMSessionObserver *); + //show error statuses to the user + void showSessionStartError(const std::string& error_string, const LLUUID session_id); + void showSessionEventError(const std::string& event_string, const std::string& error_string, const LLUUID session_id); + void showSessionForceClose(const std::string& reason, const LLUUID session_id); + static bool onConfirmForceCloseError(const LLSD& notification, const LLSD& response); + /** * Start call in a session * @return false if voice channel doesn't exist -- cgit v1.3 From 86921724c438e31957bc803695054bba80645df4 Mon Sep 17 00:00:00 2001 From: Igor Borovkov Date: Wed, 28 Oct 2009 18:31:43 +0200 Subject: IM refactoring: moved speakers management stuff in its own files llspeakers.* from dying llfloateractivespeakers. * Decresed number of llfloateractivespeakers.h includes to a minimum. --HG-- branch : product-engine --- indra/newview/CMakeLists.txt | 2 + indra/newview/llagent.cpp | 2 +- indra/newview/llimpanel.cpp | 1 + indra/newview/llimview.h | 2 +- indra/newview/llnearbychatbar.h | 2 +- indra/newview/llparticipantlist.cpp | 2 +- indra/newview/llspeakers.cpp | 639 ++++++++++++++++++++++++++++++++++++ indra/newview/llspeakers.h | 172 ++++++++++ indra/newview/llviewermessage.cpp | 1 - indra/newview/llvoicechannel.h | 1 + 10 files changed, 819 insertions(+), 5 deletions(-) create mode 100644 indra/newview/llspeakers.cpp create mode 100644 indra/newview/llspeakers.h (limited to 'indra/newview/llimpanel.cpp') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index d81ce0c4db..5210ff66ed 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -371,6 +371,7 @@ set(viewer_SOURCE_FILES llsky.cpp llslurl.cpp llspatialpartition.cpp + llspeakers.cpp llsplitbutton.cpp llsprite.cpp llstartup.cpp @@ -848,6 +849,7 @@ set(viewer_HEADER_FILES llsky.h llslurl.h llspatialpartition.h + llspeakers.h llsplitbutton.h llsprite.h llstartup.h diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 5800db482f..75a72e5b17 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -42,7 +42,7 @@ #include "lldrawable.h" #include "llfirstuse.h" #include "llfloaterreg.h" -#include "llfloateractivespeakers.h" +#include "llspeakers.h" #include "llfloatercamera.h" #include "llfloatercustomize.h" diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 211e657a76..77ee90f681 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -54,6 +54,7 @@ #include "llconsole.h" #include "llgroupactions.h" #include "llfloater.h" +#include "llfloateractivespeakers.h" #include "llfloatercall.h" #include "llavataractions.h" #include "llimview.h" diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 80b54d7ef0..8c2ca149a6 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -34,7 +34,7 @@ #define LL_LLIMVIEW_H #include "lldarray.h" -#include "llfloateractivespeakers.h" //for LLIMSpeakerMgr +#include "llspeakers.h" //for LLIMSpeakerMgr #include "llimpanel.h" //for voice channels #include "llmodaldialog.h" #include "llinstantmessage.h" diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h index d495a10193..06204e6367 100644 --- a/indra/newview/llnearbychatbar.h +++ b/indra/newview/llnearbychatbar.h @@ -40,7 +40,7 @@ #include "llchiclet.h" #include "llvoiceclient.h" #include "lloutputmonitorctrl.h" -#include "llfloateractivespeakers.h" +#include "llspeakers.h" class LLGestureComboBox : public LLComboBox diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index a8c66f08ac..e97eb1df2b 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -34,7 +34,7 @@ #include "llparticipantlist.h" #include "llavatarlist.h" -#include "llfloateractivespeakers.h" +#include "llspeakers.h" //LLParticipantList retrieves add, clear and remove events and updates view accordingly LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* avatar_list): diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp new file mode 100644 index 0000000000..2341fcfc6d --- /dev/null +++ b/indra/newview/llspeakers.cpp @@ -0,0 +1,639 @@ +/** + * @file llspeakers.cpp + * @brief Management interface for muting and controlling volume of residents currently speaking + * + * $LicenseInfo:firstyear=2005&license=viewergpl$ + * + * Copyright (c) 2005-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llspeakers.h" + +#include "llagent.h" +#include "llappviewer.h" +#include "llmutelist.h" +#include "llsdutil.h" +#include "lluicolortable.h" +#include "llviewerobjectlist.h" +#include "llvoavatar.h" +#include "llworld.h" + +const F32 SPEAKER_TIMEOUT = 10.f; // seconds of not being on voice channel before removed from list of active speakers +const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f); +const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f); + +LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerType type) : + mStatus(LLSpeaker::STATUS_TEXT_ONLY), + mLastSpokeTime(0.f), + mSpeechVolume(0.f), + mHasSpoken(FALSE), + mHasLeftCurrentCall(FALSE), + mDotColor(LLColor4::white), + mID(id), + mTyping(FALSE), + mSortIndex(0), + mType(type), + mIsModerator(FALSE), + mModeratorMutedVoice(FALSE), + mModeratorMutedText(FALSE) +{ + if (name.empty() && type == SPEAKER_AGENT) + { + lookupName(); + } + else + { + mDisplayName = name; + } + + gVoiceClient->setUserVolume(id, LLMuteList::getInstance()->getSavedResidentVolume(id)); + + mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); +} + + +void LLSpeaker::lookupName() +{ + gCacheName->get(mID, FALSE, boost::bind(&LLSpeaker::onAvatarNameLookup, this, _1, _2, _3, _4)); +} + +void LLSpeaker::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group) +{ + mDisplayName = first + " " + last; +} + +LLSpeakerTextModerationEvent::LLSpeakerTextModerationEvent(LLSpeaker* source) +: LLEvent(source, "Speaker text moderation event") +{ +} + +LLSD LLSpeakerTextModerationEvent::getValue() +{ + return std::string("text"); +} + + +LLSpeakerVoiceModerationEvent::LLSpeakerVoiceModerationEvent(LLSpeaker* source) +: LLEvent(source, "Speaker voice moderation event") +{ +} + +LLSD LLSpeakerVoiceModerationEvent::getValue() +{ + return std::string("voice"); +} + +LLSpeakerListChangeEvent::LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id) +: LLEvent(source, "Speaker added/removed from speaker mgr"), + mSpeakerID(speaker_id) +{ +} + +LLSD LLSpeakerListChangeEvent::getValue() +{ + return mSpeakerID; +} + +// helper sort class +struct LLSortRecentSpeakers +{ + bool operator()(const LLPointer lhs, const LLPointer rhs) const; +}; + +bool LLSortRecentSpeakers::operator()(const LLPointer lhs, const LLPointer rhs) const +{ + // Sort first on status + if (lhs->mStatus != rhs->mStatus) + { + return (lhs->mStatus < rhs->mStatus); + } + + // and then on last speaking time + if(lhs->mLastSpokeTime != rhs->mLastSpokeTime) + { + return (lhs->mLastSpokeTime > rhs->mLastSpokeTime); + } + + // and finally (only if those are both equal), on name. + return( lhs->mDisplayName.compare(rhs->mDisplayName) < 0 ); +} + + +// +// LLSpeakerMgr +// + +LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) : + mVoiceChannel(channelp) +{ +} + +LLSpeakerMgr::~LLSpeakerMgr() +{ +} + +LLPointer LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type) +{ + if (id.isNull()) return NULL; + + LLPointer speakerp; + if (mSpeakers.find(id) == mSpeakers.end()) + { + speakerp = new LLSpeaker(id, name, type); + speakerp->mStatus = status; + mSpeakers.insert(std::make_pair(speakerp->mID, speakerp)); + mSpeakersSorted.push_back(speakerp); + fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "add"); + } + else + { + speakerp = findSpeaker(id); + if (speakerp.notNull()) + { + // keep highest priority status (lowest value) instead of overriding current value + speakerp->mStatus = llmin(speakerp->mStatus, status); + speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + // RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id + // we need to override speakers that we think are objects when we find out they are really + // residents + if (type == LLSpeaker::SPEAKER_AGENT) + { + speakerp->mType = LLSpeaker::SPEAKER_AGENT; + speakerp->lookupName(); + } + } + } + + return speakerp; +} + +void LLSpeakerMgr::update(BOOL resort_ok) +{ + if (!gVoiceClient) + { + return; + } + + LLColor4 speaking_color = LLUIColorTable::instance().getColor("SpeakingColor"); + LLColor4 overdriven_color = LLUIColorTable::instance().getColor("OverdrivenColor"); + + if(resort_ok) // only allow list changes when user is not interacting with it + { + updateSpeakerList(); + } + + // update status of all current speakers + BOOL voice_channel_active = (!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()); + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end();) + { + LLUUID speaker_id = speaker_it->first; + LLSpeaker* speakerp = speaker_it->second; + + speaker_map_t::iterator cur_speaker_it = speaker_it++; + + if (voice_channel_active && gVoiceClient->getVoiceEnabled(speaker_id)) + { + speakerp->mSpeechVolume = gVoiceClient->getCurrentPower(speaker_id); + BOOL moderator_muted_voice = gVoiceClient->getIsModeratorMuted(speaker_id); + if (moderator_muted_voice != speakerp->mModeratorMutedVoice) + { + speakerp->mModeratorMutedVoice = moderator_muted_voice; + speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp)); + } + + if (gVoiceClient->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice) + { + speakerp->mStatus = LLSpeaker::STATUS_MUTED; + } + else if (gVoiceClient->getIsSpeaking(speaker_id)) + { + // reset inactivity expiration + if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING) + { + speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); + speakerp->mHasSpoken = TRUE; + } + speakerp->mStatus = LLSpeaker::STATUS_SPEAKING; + // interpolate between active color and full speaking color based on power of speech output + speakerp->mDotColor = speaking_color; + if (speakerp->mSpeechVolume > LLVoiceClient::OVERDRIVEN_POWER_LEVEL) + { + speakerp->mDotColor = overdriven_color; + } + } + else + { + speakerp->mSpeechVolume = 0.f; + speakerp->mDotColor = ACTIVE_COLOR; + + if (speakerp->mHasSpoken) + { + // have spoken once, not currently speaking + speakerp->mStatus = LLSpeaker::STATUS_HAS_SPOKEN; + } + else + { + // default state for being in voice channel + speakerp->mStatus = LLSpeaker::STATUS_VOICE_ACTIVE; + } + } + } + // speaker no longer registered in voice channel, demote to text only + else if (speakerp->mStatus != LLSpeaker::STATUS_NOT_IN_CHANNEL) + { + if(speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL) + { + // external speakers should be timed out when they leave the voice channel (since they only exist via SLVoice) + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + } + else + { + speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY; + speakerp->mSpeechVolume = 0.f; + speakerp->mDotColor = ACTIVE_COLOR; + } + } + } + + if(resort_ok) // only allow list changes when user is not interacting with it + { + // sort by status then time last spoken + std::sort(mSpeakersSorted.begin(), mSpeakersSorted.end(), LLSortRecentSpeakers()); + } + + // for recent speakers who are not currently speaking, show "recent" color dot for most recent + // fading to "active" color + + S32 recent_speaker_count = 0; + S32 sort_index = 0; + speaker_list_t::iterator sorted_speaker_it; + for(sorted_speaker_it = mSpeakersSorted.begin(); + sorted_speaker_it != mSpeakersSorted.end(); ) + { + LLPointer speakerp = *sorted_speaker_it; + + // color code recent speakers who are not currently speaking + if (speakerp->mStatus == LLSpeaker::STATUS_HAS_SPOKEN) + { + speakerp->mDotColor = lerp(speaking_color, ACTIVE_COLOR, clamp_rescale((F32)recent_speaker_count, -2.f, 3.f, 0.f, 1.f)); + recent_speaker_count++; + } + + // stuff sort ordinal into speaker so the ui can sort by this value + speakerp->mSortIndex = sort_index++; + + // remove speakers that have been gone too long + if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL && speakerp->mActivityTimer.hasExpired()) + { + fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "remove"); + + mSpeakers.erase(speakerp->mID); + sorted_speaker_it = mSpeakersSorted.erase(sorted_speaker_it); + } + else + { + ++sorted_speaker_it; + } + } +} + +void LLSpeakerMgr::updateSpeakerList() +{ + // are we bound to the currently active voice channel? + if ((!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive())) + { + LLVoiceClient::participantMap* participants = gVoiceClient->getParticipantList(); + if(participants) + { + LLVoiceClient::participantMap::iterator participant_it; + + // add new participants to our list of known speakers + for (participant_it = participants->begin(); participant_it != participants->end(); ++participant_it) + { + LLVoiceClient::participantState* participantp = participant_it->second; + setSpeaker(participantp->mAvatarID, participantp->mDisplayName, LLSpeaker::STATUS_VOICE_ACTIVE, (participantp->isAvatar()?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL)); + } + } + } +} + +LLPointer LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id) +{ + speaker_map_t::iterator found_it = mSpeakers.find(speaker_id); + if (found_it == mSpeakers.end()) + { + return NULL; + } + return found_it->second; +} + +void LLSpeakerMgr::getSpeakerList(speaker_list_t* speaker_list, BOOL include_text) +{ + speaker_list->clear(); + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) + { + LLPointer speakerp = speaker_it->second; + // what about text only muted or inactive? + if (include_text || speakerp->mStatus != LLSpeaker::STATUS_TEXT_ONLY) + { + speaker_list->push_back(speakerp); + } + } +} + +const LLUUID LLSpeakerMgr::getSessionID() +{ + return mVoiceChannel->getSessionID(); +} + + +void LLSpeakerMgr::setSpeakerTyping(const LLUUID& speaker_id, BOOL typing) +{ + LLPointer speakerp = findSpeaker(speaker_id); + if (speakerp.notNull()) + { + speakerp->mTyping = typing; + } +} + +// speaker has chatted via either text or voice +void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id) +{ + LLPointer speakerp = findSpeaker(speaker_id); + if (speakerp.notNull()) + { + speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); + speakerp->mHasSpoken = TRUE; + } +} + +BOOL LLSpeakerMgr::isVoiceActive() +{ + // mVoiceChannel = NULL means current voice channel, whatever it is + return LLVoiceClient::voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive(); +} + + +// +// LLIMSpeakerMgr +// +LLIMSpeakerMgr::LLIMSpeakerMgr(LLVoiceChannel* channel) : LLSpeakerMgr(channel) +{ +} + +void LLIMSpeakerMgr::updateSpeakerList() +{ + // don't do normal updates which are pulled from voice channel + // rely on user list reported by sim + + // We need to do this to allow PSTN callers into group chats to show in the list. + LLSpeakerMgr::updateSpeakerList(); + + return; +} + +void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers) +{ + if ( !speakers.isMap() ) return; + + if ( speakers.has("agent_info") && speakers["agent_info"].isMap() ) + { + LLSD::map_const_iterator speaker_it; + for(speaker_it = speakers["agent_info"].beginMap(); + speaker_it != speakers["agent_info"].endMap(); + ++speaker_it) + { + LLUUID agent_id(speaker_it->first); + + LLPointer speakerp = setSpeaker( + agent_id, + LLStringUtil::null, + LLSpeaker::STATUS_TEXT_ONLY); + + if ( speaker_it->second.isMap() ) + { + speakerp->mIsModerator = speaker_it->second["is_moderator"]; + speakerp->mModeratorMutedText = + speaker_it->second["mutes"]["text"]; + } + } + } + else if ( speakers.has("agents" ) && speakers["agents"].isArray() ) + { + //older, more decprecated way. Need here for + //using older version of servers + LLSD::array_const_iterator speaker_it; + for(speaker_it = speakers["agents"].beginArray(); + speaker_it != speakers["agents"].endArray(); + ++speaker_it) + { + const LLUUID agent_id = (*speaker_it).asUUID(); + + LLPointer speakerp = setSpeaker( + agent_id, + LLStringUtil::null, + LLSpeaker::STATUS_TEXT_ONLY); + } + } +} + +void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) +{ + if ( !update.isMap() ) return; + + if ( update.has("agent_updates") && update["agent_updates"].isMap() ) + { + LLSD::map_const_iterator update_it; + for( + update_it = update["agent_updates"].beginMap(); + update_it != update["agent_updates"].endMap(); + ++update_it) + { + LLUUID agent_id(update_it->first); + LLPointer speakerp = findSpeaker(agent_id); + + LLSD agent_data = update_it->second; + + if (agent_data.isMap() && agent_data.has("transition")) + { + if (agent_data["transition"].asString() == "LEAVE" && speakerp.notNull()) + { + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + speakerp->mDotColor = INACTIVE_COLOR; + speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + } + else if (agent_data["transition"].asString() == "ENTER") + { + // add or update speaker + speakerp = setSpeaker(agent_id); + } + else + { + llwarns << "bad membership list update " << ll_print_sd(agent_data["transition"]) << llendl; + } + } + + if (speakerp.isNull()) continue; + + // should have a valid speaker from this point on + if (agent_data.isMap() && agent_data.has("info")) + { + LLSD agent_info = agent_data["info"]; + + if (agent_info.has("is_moderator")) + { + speakerp->mIsModerator = agent_info["is_moderator"]; + } + + if (agent_info.has("mutes")) + { + speakerp->mModeratorMutedText = agent_info["mutes"]["text"]; + } + } + } + } + else if ( update.has("updates") && update["updates"].isMap() ) + { + LLSD::map_const_iterator update_it; + for ( + update_it = update["updates"].beginMap(); + update_it != update["updates"].endMap(); + ++update_it) + { + LLUUID agent_id(update_it->first); + LLPointer speakerp = findSpeaker(agent_id); + + std::string agent_transition = update_it->second.asString(); + if (agent_transition == "LEAVE" && speakerp.notNull()) + { + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + speakerp->mDotColor = INACTIVE_COLOR; + speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + } + else if ( agent_transition == "ENTER") + { + // add or update speaker + speakerp = setSpeaker(agent_id); + } + else + { + llwarns << "bad membership list update " + << agent_transition << llendl; + } + } + } +} + + +// +// LLActiveSpeakerMgr +// + +LLActiveSpeakerMgr::LLActiveSpeakerMgr() : LLSpeakerMgr(NULL) +{ +} + +void LLActiveSpeakerMgr::updateSpeakerList() +{ + // point to whatever the current voice channel is + mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); + + // always populate from active voice channel + if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) + { + fireEvent(new LLSpeakerListChangeEvent(this, LLUUID::null), "clear"); + mSpeakers.clear(); + mSpeakersSorted.clear(); + mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); + } + LLSpeakerMgr::updateSpeakerList(); + + // clean up text only speakers + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) + { + LLUUID speaker_id = speaker_it->first; + LLSpeaker* speakerp = speaker_it->second; + if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) + { + // automatically flag text only speakers for removal + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + } + } + +} + + + +// +// LLLocalSpeakerMgr +// + +LLLocalSpeakerMgr::LLLocalSpeakerMgr() : LLSpeakerMgr(LLVoiceChannelProximal::getInstance()) +{ +} + +LLLocalSpeakerMgr::~LLLocalSpeakerMgr () +{ +} + +void LLLocalSpeakerMgr::updateSpeakerList() +{ + // pull speakers from voice channel + LLSpeakerMgr::updateSpeakerList(); + + if (gDisconnected)//the world is cleared. + { + return ; + } + + // pick up non-voice speakers in chat range + std::vector avatar_ids; + std::vector positions; + LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS); + for(U32 i=0; ifirst; + LLSpeaker* speakerp = speaker_it->second; + if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) + { + LLVOAvatar* avatarp = (LLVOAvatar*)gObjectList.findObject(speaker_id); + if (!avatarp || dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS) + { + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + speakerp->mDotColor = INACTIVE_COLOR; + speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + } + } + } +} diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h new file mode 100644 index 0000000000..e0f22bff4f --- /dev/null +++ b/indra/newview/llspeakers.h @@ -0,0 +1,172 @@ +/** + * @file llspeakers.h + * @brief Management interface for muting and controlling volume of residents currently speaking + * + * $LicenseInfo:firstyear=2005&license=viewergpl$ + * + * Copyright (c) 2005-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLSPEAKERS_H +#define LL_LLSPEAKERS_H + +#include "llevent.h" +#include "llspeakers.h" +#include "llvoicechannel.h" + +class LLSpeakerMgr; + +// data for a given participant in a voice channel +class LLSpeaker : public LLRefCount, public LLOldEvents::LLObservable, public LLHandleProvider, public boost::signals2::trackable +{ +public: + typedef enum e_speaker_type + { + SPEAKER_AGENT, + SPEAKER_OBJECT, + SPEAKER_EXTERNAL // Speaker that doesn't map to an avatar or object (i.e. PSTN caller in a group) + } ESpeakerType; + + typedef enum e_speaker_status + { + STATUS_SPEAKING, + STATUS_HAS_SPOKEN, + STATUS_VOICE_ACTIVE, + STATUS_TEXT_ONLY, + STATUS_NOT_IN_CHANNEL, + STATUS_MUTED + } ESpeakerStatus; + + + LLSpeaker(const LLUUID& id, const std::string& name = LLStringUtil::null, const ESpeakerType type = SPEAKER_AGENT); + ~LLSpeaker() {}; + void lookupName(); + + void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); + + ESpeakerStatus mStatus; // current activity status in speech group + F32 mLastSpokeTime; // timestamp when this speaker last spoke + F32 mSpeechVolume; // current speech amplitude (timea average rms amplitude?) + std::string mDisplayName; // cache user name for this speaker + LLFrameTimer mActivityTimer; // time out speakers when they are not part of current voice channel + BOOL mHasSpoken; // has this speaker said anything this session? + BOOL mHasLeftCurrentCall; // has this speaker left the current voice call? + LLColor4 mDotColor; + LLUUID mID; + BOOL mTyping; + S32 mSortIndex; + ESpeakerType mType; + BOOL mIsModerator; + BOOL mModeratorMutedVoice; + BOOL mModeratorMutedText; +}; + +class LLSpeakerTextModerationEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerTextModerationEvent(LLSpeaker* source); + /*virtual*/ LLSD getValue(); +}; + +class LLSpeakerVoiceModerationEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerVoiceModerationEvent(LLSpeaker* source); + /*virtual*/ LLSD getValue(); +}; + +class LLSpeakerListChangeEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id); + /*virtual*/ LLSD getValue(); + +private: + const LLUUID& mSpeakerID; +}; + +class LLSpeakerMgr : public LLOldEvents::LLObservable +{ +public: + LLSpeakerMgr(LLVoiceChannel* channelp); + virtual ~LLSpeakerMgr(); + + LLPointer findSpeaker(const LLUUID& avatar_id); + void update(BOOL resort_ok); + void setSpeakerTyping(const LLUUID& speaker_id, BOOL typing); + void speakerChatted(const LLUUID& speaker_id); + LLPointer setSpeaker(const LLUUID& id, + const std::string& name = LLStringUtil::null, + LLSpeaker::ESpeakerStatus status = LLSpeaker::STATUS_TEXT_ONLY, + LLSpeaker::ESpeakerType = LLSpeaker::SPEAKER_AGENT); + + BOOL isVoiceActive(); + + typedef std::vector > speaker_list_t; + void getSpeakerList(speaker_list_t* speaker_list, BOOL include_text); + LLVoiceChannel* getVoiceChannel() { return mVoiceChannel; } + const LLUUID getSessionID(); + +protected: + virtual void updateSpeakerList(); + + typedef std::map > speaker_map_t; + speaker_map_t mSpeakers; + + speaker_list_t mSpeakersSorted; + LLFrameTimer mSpeechTimer; + LLVoiceChannel* mVoiceChannel; +}; + +class LLIMSpeakerMgr : public LLSpeakerMgr +{ +public: + LLIMSpeakerMgr(LLVoiceChannel* channel); + + void updateSpeakers(const LLSD& update); + void setSpeakers(const LLSD& speakers); +protected: + virtual void updateSpeakerList(); +}; + +class LLActiveSpeakerMgr : public LLSpeakerMgr, public LLSingleton +{ +public: + LLActiveSpeakerMgr(); +protected: + virtual void updateSpeakerList(); +}; + +class LLLocalSpeakerMgr : public LLSpeakerMgr, public LLSingleton +{ +public: + LLLocalSpeakerMgr(); + ~LLLocalSpeakerMgr (); +protected: + virtual void updateSpeakerList(); +}; + +#endif // LL_LLSPEAKERS_H diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 791ec07349..a171d66d5c 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -72,7 +72,6 @@ #include "llviewercontrol.h" #include "lldrawpool.h" #include "llfirstuse.h" -#include "llfloateractivespeakers.h" #include "llfloateranimpreview.h" #include "llfloaterbuycurrency.h" #include "llfloaterbuyland.h" diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 4a9100fba6..9966bdd5ab 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -33,6 +33,7 @@ #ifndef LL_VOICECHANNEL_H #define LL_VOICECHANNEL_H +#include "llhandle.h" #include "llvoiceclient.h" class LLPanel; -- cgit v1.3