diff options
Diffstat (limited to 'indra/newview')
23 files changed, 1386 insertions, 1171 deletions
| diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index dd3937a6ef..d81ce0c4db 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -484,6 +484,7 @@ set(viewer_SOURCE_FILES      llvoclouds.cpp      llvograss.cpp      llvoground.cpp +    llvoicechannel.cpp      llvoiceclient.cpp      llvoiceremotectrl.cpp      llvoicevisualizer.cpp @@ -960,6 +961,7 @@ set(viewer_HEADER_FILES      llvoclouds.h      llvograss.h      llvoground.h +    llvoicechannel.h      llvoiceclient.h      llvoiceremotectrl.h      llvoicevisualizer.h diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 923a66ee8e..2ff414344a 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -49,7 +49,6 @@  #include "llviewerstats.h"  #include "llmd5.h"  #include "llpumpio.h" -#include "llimpanel.h"  #include "llmimetypes.h"  #include "llslurl.h"  #include "llstartup.h" @@ -76,6 +75,7 @@  #include "llteleporthistory.h"  #include "lllocationhistory.h"  #include "llfasttimerview.h" +#include "llvoicechannel.h"  #include "llweb.h"  #include "llsecondlifeurls.h" diff --git a/indra/newview/llfloaterchatterbox.cpp b/indra/newview/llfloaterchatterbox.cpp index dea656b0e4..fbf09207fe 100644 --- a/indra/newview/llfloaterchatterbox.cpp +++ b/indra/newview/llfloaterchatterbox.cpp @@ -42,7 +42,7 @@  #include "llfloaterfriends.h"  #include "llfloatergroups.h"  #include "llviewercontrol.h" -#include "llimview.h" +#include "llvoicechannel.h"  #include "llimpanel.h"  // diff --git a/indra/newview/llfloaterinventory.cpp b/indra/newview/llfloaterinventory.cpp index c890f9f122..a47916b7d7 100644 --- a/indra/newview/llfloaterinventory.cpp +++ b/indra/newview/llfloaterinventory.cpp @@ -1438,7 +1438,11 @@ void LLInventoryPanel::modelChanged(U32 mask)  						}  						LLFolderViewFolder* new_parent = (LLFolderViewFolder*)mFolders->getItemByID(model_item->getParentUUID()); -						if (view_item->getParentFolder() != new_parent) + +						// added check against NULL for cases when Inventory panel contains startFolder. +						// in this case parent is LLFolderView (LLInventoryPanel::mFolders) itself. +						// this check is a fix for bug EXT-1859. +						if (NULL != new_parent && view_item->getParentFolder() != new_parent)  						{  							view_item->getParentFolder()->extractItem(view_item);  							view_item->addToFolder(new_parent, mFolders); diff --git a/indra/newview/llfloatervoicedevicesettings.cpp b/indra/newview/llfloatervoicedevicesettings.cpp index b64257b11d..aca9198f59 100644 --- a/indra/newview/llfloatervoicedevicesettings.cpp +++ b/indra/newview/llfloatervoicedevicesettings.cpp @@ -43,7 +43,7 @@  #include "llsliderctrl.h"  #include "llviewercontrol.h"  #include "llvoiceclient.h" -#include "llimpanel.h" +#include "llvoicechannel.h"  // Library includes (after viewer)  #include "lluictrlfactory.h" diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 91e383eb14..9e92e2f490 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -42,7 +42,6 @@  #include "llchiclet.h"  #include "llfloaterchat.h"  #include "llfloaterreg.h" -#include "llimview.h"  #include "lllineeditor.h"  #include "lllogchat.h"  #include "llpanelimcontrolpanel.h" @@ -50,6 +49,7 @@  #include "lltrans.h"  #include "llchathistory.h"  #include "llviewerwindow.h" +#include "llvoicechannel.h"  #include "lltransientfloatermgr.h" @@ -202,9 +202,10 @@ BOOL LLIMFloater::postBuild()  	if (other_party_id.notNull())  	{  		mOtherParticipantUUID = other_party_id; -		mControlPanel->setID(mOtherParticipantUUID);  	} +	mControlPanel->setSessionId(mSessionID); +  	LLButton* slide_left = getChild<LLButton>("slide_left_btn");  	slide_left->setVisible(mControlPanel->getVisible());  	slide_left->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); @@ -248,6 +249,8 @@ BOOL LLIMFloater::postBuild()  // virtual  void LLIMFloater::draw()  { + +	  	if ( mMeTyping )  	{  		// Time out if user hasn't typed for a while. @@ -403,10 +406,12 @@ void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id)  {  	mSessionInitialized = true; +	//will be different only for an ad-hoc im session  	if (mSessionID != im_session_id)  	{  		mSessionID = im_session_id;  		setKey(im_session_id); +		mControlPanel->setSessionId(im_session_id);  	}  	//*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB) diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 163984f740..211e657a76 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 @@ -1836,75 +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 4e306c7fab..39107d9a22 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -50,133 +50,6 @@ class LLIMSpeakerMgr;  class LLPanelActiveSpeakers;  class LLPanelChatControlPanel; -class LLVoiceChannel : public LLVoiceClientStatusObserver -{ -public: -	typedef enum e_voice_channel_state -	{ -		STATE_NO_CHANNEL_INFO, -		STATE_ERROR, -		STATE_HUNG_UP, -		STATE_READY, -		STATE_CALL_STARTED, -		STATE_RINGING, -		STATE_CONNECTED -	} EState; - -	LLVoiceChannel(const LLUUID& session_id, const std::string& session_name); -	virtual ~LLVoiceChannel(); - -	/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); - -	virtual void handleStatusChange(EStatusType status); -	virtual void handleError(EStatusType status); -	virtual void deactivate(); -	virtual void activate(); -	virtual void setChannelInfo( -		const std::string& uri, -		const std::string& credentials); -	virtual void getChannelInfo(); -	virtual BOOL isActive(); -	virtual BOOL callStarted(); -	const std::string& getSessionName() const { return mSessionName; } - -	const LLUUID getSessionID() { return mSessionID; } -	EState getState() { return mState; } - -	void updateSessionID(const LLUUID& new_session_id); -	const LLSD& getNotifyArgs() { return mNotifyArgs; } - -	static LLVoiceChannel* getChannelByID(const LLUUID& session_id); -	static LLVoiceChannel* getChannelByURI(std::string uri); -	static LLVoiceChannel* getCurrentVoiceChannel() { return sCurrentVoiceChannel; } -	static void initClass(); -	 -	static void suspend(); -	static void resume(); - -protected: -	virtual void setState(EState state); -	void toggleCallWindowIfNeeded(EState state); -	void setURI(std::string uri); - -	std::string	mURI; -	std::string	mCredentials; -	LLUUID		mSessionID; -	EState		mState; -	std::string	mSessionName; -	LLSD mNotifyArgs; -	BOOL		mIgnoreNextSessionLeave; -	LLHandle<LLPanel> mLoginNotificationHandle; - -	typedef std::map<LLUUID, LLVoiceChannel*> voice_channel_map_t; -	static voice_channel_map_t sVoiceChannelMap; - -	typedef std::map<std::string, LLVoiceChannel*> voice_channel_map_uri_t; -	static voice_channel_map_uri_t sVoiceChannelURIMap; - -	static LLVoiceChannel* sCurrentVoiceChannel; -	static LLVoiceChannel* sSuspendedVoiceChannel; -	static BOOL sSuspended; -}; - -class LLVoiceChannelGroup : public LLVoiceChannel -{ -public: -	LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name); - -	/*virtual*/ void handleStatusChange(EStatusType status); -	/*virtual*/ void handleError(EStatusType status); -	/*virtual*/ void activate(); -	/*virtual*/ void deactivate(); -	/*vritual*/ void setChannelInfo( -		const std::string& uri, -		const std::string& credentials); -	/*virtual*/ void getChannelInfo(); - -protected: -	virtual void setState(EState state); - -private: -	U32 mRetries; -	BOOL mIsRetrying; -}; - -class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton<LLVoiceChannelProximal> -{ -public: -	LLVoiceChannelProximal(); - -	/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); -	/*virtual*/ void handleStatusChange(EStatusType status); -	/*virtual*/ void handleError(EStatusType status); -	/*virtual*/ BOOL isActive(); -	/*virtual*/ void activate(); -	/*virtual*/ void deactivate(); - -}; - -class LLVoiceChannelP2P : public LLVoiceChannelGroup -{ -public: -	LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id); - -	/*virtual*/ void handleStatusChange(EStatusType status); -	/*virtual*/ void handleError(EStatusType status); -    /*virtual*/ void activate(); -	/*virtual*/ void getChannelInfo(); - -	void setSessionHandle(const std::string& handle, const std::string &inURI); - -protected: -	virtual void setState(EState state); - -private: -	std::string	mSessionHandle; -	LLUUID		mOtherUserID; -	BOOL		mReceivedCall; -}; -  class LLFloaterIMPanel : public LLFloater  {  public: @@ -256,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 59cd9cec86..2e5e23c845 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -70,6 +70,7 @@  #include "llviewerwindow.h"  #include "llnotify.h"  #include "llviewerregion.h" +#include "llvoicechannel.h"  #include "lltrans.h"  #include "llrecentpeople.h" @@ -144,7 +145,8 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string&  	mInitialTargetIDs(ids),  	mVoiceChannel(NULL),  	mSpeakers(NULL), -	mSessionInitialized(false) +	mSessionInitialized(false), +	mCallBackEnabled(true)  {  	if (IM_NOTHING_SPECIAL == type || IM_SESSION_P2P_INVITE == type)  	{ @@ -168,6 +170,11 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string&  		//so we're already initialized  		mSessionInitialized = true;  	} + +	if (IM_NOTHING_SPECIAL == type) +	{ +		mCallBackEnabled = LLVoiceClient::getInstance()->isSessionCallBackPossible(mSessionID); +	}  }  LLIMModel::LLIMSession::~LLIMSession() @@ -883,20 +890,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);  			}  		}  	} @@ -948,6 +946,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  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2026,15 +2124,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); @@ -2067,15 +2158,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);  		}  	}  }; @@ -2093,13 +2179,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..f09c5a9521 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -70,6 +70,10 @@ public:  		LLIMSpeakerMgr* mSpeakers;  		bool mSessionInitialized; + +		//true if calling back the session URI after the session has closed is possible. +		//Currently this will be false only for PSTN P2P calls. +		bool mCallBackEnabled;  	}; @@ -314,6 +318,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 diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index 7239f49b7f..543198c1d2 100644 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -86,6 +86,20 @@ bool LLTipHandler::processNotification(const LLSD& notify)  	if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change")  	{ +		// archive message in nearby chat +		LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); +		if(nearby_chat) +		{ +			LLChat chat_msg(notification->getMessage()); +			nearby_chat->addMessage(chat_msg); + +			// don't show toast if Nearby Chat is opened +			if (nearby_chat->getVisible()) +			{ +				return true; +			}			 +		} +  		LLToastNotifyPanel* notify_box = new LLToastNotifyPanel(notification);  		LLToast::Params p; @@ -99,14 +113,6 @@ bool LLTipHandler::processNotification(const LLSD& notify)  		LLScreenChannel* channel = dynamic_cast<LLScreenChannel*>(mChannel);  		if(channel)  			channel->addToast(p); - -		// archive message in nearby chat -		LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); -		if(nearby_chat) -		{ -			LLChat chat_msg(notification->getMessage()); -			nearby_chat->addMessage(chat_msg); -		}  	}  	else if (notify["sigtype"].asString() == "delete")  	{ diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index 6eed956eb8..6678a3a460 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -34,6 +34,7 @@  #include "llpanelimcontrolpanel.h" +#include "llagent.h"  #include "llavataractions.h"  #include "llavatariconctrl.h"  #include "llbutton.h" @@ -41,6 +42,53 @@  #include "llavatarlist.h"  #include "llparticipantlist.h"  #include "llimview.h" +#include "llvoicechannel.h" + +void LLPanelChatControlPanel::onCallButtonClicked() +{ +	gIMMgr->startCall(mSessionId); +} + +void LLPanelChatControlPanel::onEndCallButtonClicked() +{ +	gIMMgr->endCall(mSessionId); +} + +BOOL LLPanelChatControlPanel::postBuild() +{ +	childSetAction("call_btn", boost::bind(&LLPanelChatControlPanel::onCallButtonClicked, this)); +	childSetAction("end_call_btn", boost::bind(&LLPanelChatControlPanel::onEndCallButtonClicked, this)); + +	return TRUE; +} + +void LLPanelChatControlPanel::draw() +{ +	// hide/show start call and end call buttons +	bool voice_enabled = LLVoiceClient::voiceEnabled(); + +	LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionId); +	if (!session) return; + +	LLVoiceChannel* voice_channel = session->mVoiceChannel; +	if (voice_channel && voice_enabled) +	{ +		childSetVisible("end_call_btn", voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); +		childSetVisible("call_btn", voice_channel->getState() < LLVoiceChannel::STATE_CALL_STARTED); +	} + +	bool session_initialized = session->mSessionInitialized; +	bool callback_enabled = session->mCallBackEnabled; +	LLViewerRegion* region = gAgent.getRegion(); + +	BOOL enable_connect = (region && region->getCapability("ChatSessionRequest") != "") +		&& session_initialized +		&& voice_enabled +		&& callback_enabled; +	childSetEnabled("call_btn", enable_connect); + +	LLPanel::draw(); +}  LLPanelIMControlPanel::LLPanelIMControlPanel()  { @@ -54,11 +102,11 @@ BOOL LLPanelIMControlPanel::postBuild()  {  	childSetAction("view_profile_btn", boost::bind(&LLPanelIMControlPanel::onViewProfileButtonClicked, this));  	childSetAction("add_friend_btn", boost::bind(&LLPanelIMControlPanel::onAddFriendButtonClicked, this)); -	childSetAction("call_btn", boost::bind(&LLPanelIMControlPanel::onCallButtonClicked, this)); +  	childSetAction("share_btn", boost::bind(&LLPanelIMControlPanel::onShareButtonClicked, this));  	childSetEnabled("add_friend_btn", !LLAvatarActions::isFriend(getChild<LLAvatarIconCtrl>("avatar_icon")->getAvatarId())); - -	return TRUE; +	 +	return LLPanelChatControlPanel::postBuild();  }  void LLPanelIMControlPanel::onViewProfileButtonClicked() @@ -73,21 +121,20 @@ void LLPanelIMControlPanel::onAddFriendButtonClicked()  	LLAvatarActions::requestFriendshipDialog(avatar_icon->getAvatarId(), full_name);  } -void LLPanelIMControlPanel::onCallButtonClicked() -{ -	// *TODO: Implement -} -  void LLPanelIMControlPanel::onShareButtonClicked()  {  	// *TODO: Implement  } -void LLPanelIMControlPanel::setID(const LLUUID& avatar_id) +void LLPanelIMControlPanel::setSessionId(const LLUUID& session_id)  { +	LLPanelChatControlPanel::setSessionId(session_id); + +	LLUUID avatar_id = LLIMModel::getInstance()->getOtherParticipantID(session_id); +  	// Disable "Add friend" button for friends.  	childSetEnabled("add_friend_btn", !LLAvatarActions::isFriend(avatar_id)); - +	  	getChild<LLAvatarIconCtrl>("avatar_icon")->setValue(avatar_id);  } @@ -100,12 +147,11 @@ LLPanelGroupControlPanel::LLPanelGroupControlPanel(const LLUUID& session_id)  BOOL LLPanelGroupControlPanel::postBuild()  {  	childSetAction("group_info_btn", boost::bind(&LLPanelGroupControlPanel::onGroupInfoButtonClicked, this)); -	childSetAction("call_btn", boost::bind(&LLPanelGroupControlPanel::onCallButtonClicked, this));  	mAvatarList = getChild<LLAvatarList>("speakers_list");  	mParticipantList = new LLParticipantList(mSpeakerManager, mAvatarList); -	return TRUE; +	return LLPanelChatControlPanel::postBuild();  }  LLPanelGroupControlPanel::~LLPanelGroupControlPanel() @@ -127,13 +173,9 @@ void LLPanelGroupControlPanel::onGroupInfoButtonClicked()  } -void LLPanelGroupControlPanel::onCallButtonClicked() +void LLPanelGroupControlPanel::setSessionId(const LLUUID& session_id)  { -	// *TODO: Implement -} - +	LLPanelChatControlPanel::setSessionId(session_id); -void LLPanelGroupControlPanel::setID(const LLUUID& id) -{ -	mGroupID = id; +	mGroupID = LLIMModel::getInstance()->getOtherParticipantID(session_id);  } diff --git a/indra/newview/llpanelimcontrolpanel.h b/indra/newview/llpanelimcontrolpanel.h index 138b1630c4..00c96cf31a 100644 --- a/indra/newview/llpanelimcontrolpanel.h +++ b/indra/newview/llpanelimcontrolpanel.h @@ -45,8 +45,16 @@ public:  	LLPanelChatControlPanel() {};  	~LLPanelChatControlPanel() {}; -	// sets the group or avatar UUID -	virtual void setID(const LLUUID& avatar_id)= 0; +	virtual BOOL postBuild(); +	virtual void draw(); + +	void onCallButtonClicked(); +	void onEndCallButtonClicked(); + +	virtual void setSessionId(const LLUUID& session_id) { mSessionId = session_id; } + +private: +	LLUUID mSessionId;  }; @@ -58,13 +66,14 @@ public:  	BOOL postBuild(); -	void setID(const LLUUID& avatar_id); +	void setSessionId(const LLUUID& session_id);  private:  	void onViewProfileButtonClicked();  	void onAddFriendButtonClicked(); -	void onCallButtonClicked();  	void onShareButtonClicked(); + +	LLUUID mAvatarID;  }; @@ -76,12 +85,11 @@ public:  	BOOL postBuild(); -	void setID(const LLUUID& id); +	void setSessionId(const LLUUID& session_id);  	/*virtual*/ void draw();  private:  	void onGroupInfoButtonClicked(); -	void onCallButtonClicked();  	LLUUID mGroupID;  	LLSpeakerMgr* mSpeakerManager; diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index 83fb147a49..4650c0eab4 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -56,7 +56,7 @@  //static LLRegisterPanelClassWrapper<LLLandmarksPanel> t_landmarks("panel_landmarks");  static const std::string OPTIONS_BUTTON_NAME = "options_gear_btn"; -static const std::string ADD_LANDMARK_BUTTON_NAME = "add_landmark_btn"; +static const std::string ADD_BUTTON_NAME = "add_btn";  static const std::string ADD_FOLDER_BUTTON_NAME = "add_folder_btn";  static const std::string TRASH_BUTTON_NAME = "trash_btn"; @@ -300,6 +300,8 @@ void LLLandmarksPanel::processParcelInfo(const LLParcelData& parcel_data)  					panel_pick, panel_places,params));  			panel_pick->setSaveCallback(boost::bind(&LLLandmarksPanel::onPickPanelExit,this,  				panel_pick, panel_places,params)); +			panel_pick->setCancelCallback(boost::bind(&LLLandmarksPanel::onPickPanelExit,this, +							panel_pick, panel_places,params));  		}  	}  } @@ -433,11 +435,11 @@ void LLLandmarksPanel::initListCommandsHandlers()  	mListCommands = getChild<LLPanel>("bottom_panel");  	mListCommands->childSetAction(OPTIONS_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onActionsButtonClick, this)); -	mListCommands->childSetAction(ADD_LANDMARK_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onAddLandmarkButtonClick, this)); -	mListCommands->childSetAction(ADD_FOLDER_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onAddFolderButtonClick, this));  	mListCommands->childSetAction(TRASH_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onTrashButtonClick, this)); +	mListCommands->getChild<LLButton>(ADD_BUTTON_NAME)->setHeldDownCallback(boost::bind(&LLLandmarksPanel::onAddButtonHeldDown, this)); +	static const LLSD add_landmark_command("add_landmark"); +	mListCommands->childSetAction(ADD_BUTTON_NAME, boost::bind(&LLLandmarksPanel::onAddAction, this, add_landmark_command)); -	  	LLDragAndDropButton* trash_btn = mListCommands->getChild<LLDragAndDropButton>(TRASH_BUTTON_NAME);  	trash_btn->setDragAndDropHandler(boost::bind(&LLLandmarksPanel::handleDragAndDropToTrash, this  			,	_4 // BOOL drop @@ -453,6 +455,7 @@ void LLLandmarksPanel::initListCommandsHandlers()  	mEnableCallbackRegistrar.add("Places.LandmarksGear.Enable", boost::bind(&LLLandmarksPanel::isActionEnabled, this, _2));  	mGearLandmarkMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_places_gear_landmark.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());  	mGearFolderMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_places_gear_folder.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); +	mMenuAdd = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_place_add_button.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());  } @@ -488,52 +491,26 @@ void LLLandmarksPanel::onActionsButtonClick()  		mGearFolderMenu->getChild<LLMenuItemCallGL>("collapse")->setVisible(cur_item->isOpen());  		menu = mGearFolderMenu;  	} -	if(menu) -	{ -		menu->buildDrawLabels(); -		menu->updateParent(LLMenuGL::sMenuContainer); -		LLView* actions_btn  = getChild<LLView>(OPTIONS_BUTTON_NAME); -		S32 menu_x, menu_y; -		actions_btn->localPointToOtherView(0,actions_btn->getRect().getHeight(),&menu_x,&menu_y, this); -		menu_y += menu->getRect().getHeight(); -		LLMenuGL::showPopup(this, menu, menu_x,menu_y); -	} +	showActionMenu(menu,OPTIONS_BUTTON_NAME);  } -void LLLandmarksPanel::onAddLandmarkButtonClick() const +void LLLandmarksPanel::onAddButtonHeldDown()  { -	if(LLLandmarkActions::landmarkAlreadyExists()) -	{ -		std::string location; -		LLAgentUI::buildLocationString(location, LLAgentUI::LOCATION_FORMAT_FULL); -		llwarns<<" Landmark already exists at location:  "<< location<<llendl; -		return; -	} -	LLSideTray::getInstance()->showPanel("panel_places", LLSD().insert("type", "create_landmark")); +	showActionMenu(mMenuAdd,ADD_BUTTON_NAME);  } -void LLLandmarksPanel::onAddFolderButtonClick() const +void LLLandmarksPanel::showActionMenu(LLMenuGL* menu, std::string spawning_view_name)  { -	LLFolderViewItem*  item = getCurSelectedItem(); -	if(item &&  mCurrentSelectedList == mLandmarksInventoryPanel) +	if (menu)  	{ -		LLFolderViewEventListener* folder_bridge = NULL; -		if(item-> getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK) -		{ -			// for a landmark get parent folder bridge -			folder_bridge = item->getParentFolder()->getListener(); -		} -		else if (item-> getListener()->getInventoryType() == LLInventoryType::IT_CATEGORY)  -		{ -			// for a folder get its own bridge -			folder_bridge = item->getListener(); -		} - -		menu_create_inventory_item(mCurrentSelectedList->getRootFolder() -			, dynamic_cast<LLFolderBridge*>(folder_bridge) -			, LLSD("category") -			, gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK) -			); +		menu->buildDrawLabels(); +		menu->updateParent(LLMenuGL::sMenuContainer); +		LLView* spawning_view = getChild<LLView> (spawning_view_name); +		S32 menu_x, menu_y; +		//show menu in co-ordinates of panel +		spawning_view->localPointToOtherView(0, spawning_view->getRect().getHeight(), &menu_x, &menu_y, this); +		menu_y += menu->getRect().getHeight(); +		LLMenuGL::showPopup(this, menu, menu_x, menu_y);  	}  } @@ -547,11 +524,39 @@ void LLLandmarksPanel::onAddAction(const LLSD& userdata) const  	std::string command_name = userdata.asString();  	if("add_landmark" == command_name)  	{ -		onAddLandmarkButtonClick(); +		if(LLLandmarkActions::landmarkAlreadyExists()) +		{ +			std::string location; +			LLAgentUI::buildLocationString(location, LLAgentUI::LOCATION_FORMAT_FULL); +			llwarns<<" Landmark already exists at location:  "<< location<<llendl; +			return; +		} +		LLSideTray::getInstance()->showPanel("panel_places", LLSD().insert("type", "create_landmark"));  	}   	else if ("category" == command_name)  	{ -		onAddFolderButtonClick(); +		LLFolderViewItem* item = getCurSelectedItem(); +		if (item && mCurrentSelectedList == mLandmarksInventoryPanel) +		{ +			LLFolderViewEventListener* folder_bridge = NULL; +			if (item-> getListener()->getInventoryType() +					== LLInventoryType::IT_LANDMARK) +			{ +				// for a landmark get parent folder bridge +				folder_bridge = item->getParentFolder()->getListener(); +			} +			else if (item-> getListener()->getInventoryType() +					== LLInventoryType::IT_CATEGORY) +			{ +				// for a folder get its own bridge +				folder_bridge = item->getListener(); +			} + +			menu_create_inventory_item(mCurrentSelectedList->getRootFolder(), +					dynamic_cast<LLFolderBridge*> (folder_bridge), LLSD( +							"category"), gInventory.findCategoryUUIDForType( +							LLAssetType::AT_LANDMARK)); +		}  	}  } diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h index 47c9f7647c..958850134d 100644 --- a/indra/newview/llpanellandmarks.h +++ b/indra/newview/llpanellandmarks.h @@ -90,8 +90,8 @@ private:  	void initListCommandsHandlers();  	void updateListCommands();  	void onActionsButtonClick(); -	void onAddLandmarkButtonClick() const; -	void onAddFolderButtonClick() const; +	void showActionMenu(LLMenuGL* menu, std::string spawning_view_name); +	void onAddButtonHeldDown();  	void onTrashButtonClick() const;  	void onAddAction(const LLSD& command_name) const;  	void onClipboardAction(const LLSD& command_name) const; @@ -121,6 +121,7 @@ private:  	LLInventorySubTreePanel*	mLibraryInventoryPanel;  	LLMenuGL*					mGearLandmarkMenu;  	LLMenuGL*					mGearFolderMenu; +	LLMenuGL*					mMenuAdd;  	LLInventorySubTreePanel*	mCurrentSelectedList;  	LLPanel*					mListCommands; diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 534c69a2a3..b10eb91b46 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -48,6 +48,18 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLAvatarList* av  	mSpeakerMgr->addListener(mSpeakerAddListener, "add");  	mSpeakerMgr->addListener(mSpeakerRemoveListener, "remove");  	mSpeakerMgr->addListener(mSpeakerClearListener, "clear"); + +	//Lets fill avatarList with existing speakers +	LLAvatarList::uuid_vector_t& group_members = mAvatarList->getIDs(); + +	LLSpeakerMgr::speaker_list_t speaker_list; +	mSpeakerMgr->getSpeakerList(&speaker_list, true); +	for(LLSpeakerMgr::speaker_list_t::iterator it = speaker_list.begin(); it != speaker_list.end(); it++) +	{ +		group_members.push_back((*it)->mID); +	} +	mAvatarList->setDirty(); +	mAvatarList->sortByName();  }  LLParticipantList::~LLParticipantList() diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp new file mode 100644 index 0000000000..96fcf61e62 --- /dev/null +++ b/indra/newview/llvoicechannel.cpp @@ -0,0 +1,872 @@ +/**  + * @file llvoicechannel.cpp + * @brief Voice Channel related classes + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + *  + * Copyright (c) 2001-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 "llagent.h" +#include "llfloaterreg.h" +#include "llimview.h" +#include "llnotifications.h" +#include "llpanel.h" +#include "llrecentpeople.h" +#include "llviewercontrol.h" +#include "llvoicechannel.h" + + +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; + +// +// Constants +// +const U32 DEFAULT_RETRIES_COUNT = 3; + + +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); +} diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h new file mode 100644 index 0000000000..4a9100fba6 --- /dev/null +++ b/indra/newview/llvoicechannel.h @@ -0,0 +1,167 @@ +/**  + * @file llvoicechannel.h + * @brief Voice channel related classes + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + *  + * Copyright (c) 2001-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_VOICECHANNEL_H +#define LL_VOICECHANNEL_H + +#include "llvoiceclient.h" + +class LLPanel; + +class LLVoiceChannel : public LLVoiceClientStatusObserver +{ +public: +	typedef enum e_voice_channel_state +	{ +		STATE_NO_CHANNEL_INFO, +		STATE_ERROR, +		STATE_HUNG_UP, +		STATE_READY, +		STATE_CALL_STARTED, +		STATE_RINGING, +		STATE_CONNECTED +	} EState; + +	LLVoiceChannel(const LLUUID& session_id, const std::string& session_name); +	virtual ~LLVoiceChannel(); + +	/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); + +	virtual void handleStatusChange(EStatusType status); +	virtual void handleError(EStatusType status); +	virtual void deactivate(); +	virtual void activate(); +	virtual void setChannelInfo( +		const std::string& uri, +		const std::string& credentials); +	virtual void getChannelInfo(); +	virtual BOOL isActive(); +	virtual BOOL callStarted(); +	const std::string& getSessionName() const { return mSessionName; } + +	const LLUUID getSessionID() { return mSessionID; } +	EState getState() { return mState; } + +	void updateSessionID(const LLUUID& new_session_id); +	const LLSD& getNotifyArgs() { return mNotifyArgs; } + +	static LLVoiceChannel* getChannelByID(const LLUUID& session_id); +	static LLVoiceChannel* getChannelByURI(std::string uri); +	static LLVoiceChannel* getCurrentVoiceChannel() { return sCurrentVoiceChannel; } +	static void initClass(); +	 +	static void suspend(); +	static void resume(); + +protected: +	virtual void setState(EState state); +	void toggleCallWindowIfNeeded(EState state); +	void setURI(std::string uri); + +	std::string	mURI; +	std::string	mCredentials; +	LLUUID		mSessionID; +	EState		mState; +	std::string	mSessionName; +	LLSD mNotifyArgs; +	BOOL		mIgnoreNextSessionLeave; +	LLHandle<LLPanel> mLoginNotificationHandle; + +	typedef std::map<LLUUID, LLVoiceChannel*> voice_channel_map_t; +	static voice_channel_map_t sVoiceChannelMap; + +	typedef std::map<std::string, LLVoiceChannel*> voice_channel_map_uri_t; +	static voice_channel_map_uri_t sVoiceChannelURIMap; + +	static LLVoiceChannel* sCurrentVoiceChannel; +	static LLVoiceChannel* sSuspendedVoiceChannel; +	static BOOL sSuspended; +}; + +class LLVoiceChannelGroup : public LLVoiceChannel +{ +public: +	LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name); + +	/*virtual*/ void handleStatusChange(EStatusType status); +	/*virtual*/ void handleError(EStatusType status); +	/*virtual*/ void activate(); +	/*virtual*/ void deactivate(); +	/*vritual*/ void setChannelInfo( +		const std::string& uri, +		const std::string& credentials); +	/*virtual*/ void getChannelInfo(); + +protected: +	virtual void setState(EState state); + +private: +	U32 mRetries; +	BOOL mIsRetrying; +}; + +class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton<LLVoiceChannelProximal> +{ +public: +	LLVoiceChannelProximal(); + +	/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); +	/*virtual*/ void handleStatusChange(EStatusType status); +	/*virtual*/ void handleError(EStatusType status); +	/*virtual*/ BOOL isActive(); +	/*virtual*/ void activate(); +	/*virtual*/ void deactivate(); + +}; + +class LLVoiceChannelP2P : public LLVoiceChannelGroup +{ +public: +	LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id); + +	/*virtual*/ void handleStatusChange(EStatusType status); +	/*virtual*/ void handleError(EStatusType status); +    /*virtual*/ void activate(); +	/*virtual*/ void getChannelInfo(); + +	void setSessionHandle(const std::string& handle, const std::string &inURI); + +protected: +	virtual void setState(EState state); + +private: +	std::string	mSessionHandle; +	LLUUID		mOtherUserID; +	BOOL		mReceivedCall; +}; + +#endif  // LL_VOICECHANNEL_H diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 02f63a848b..2834284a9b 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -57,13 +57,13 @@  #include "llagent.h"  #include "llcachename.h"  #include "llimview.h" // for LLIMMgr -#include "llimpanel.h" // for LLVoiceChannel  #include "llparcel.h"  #include "llviewerparcelmgr.h"  #include "llfirstuse.h"  #include "llviewerwindow.h"  #include "llviewercamera.h"  #include "llvoavatarself.h" +#include "llvoicechannel.h"  #include "llfloaterfriends.h"  //VIVOX, inorder to refresh communicate panel  #include "llfloaterchat.h"		// for LLFloaterChat::addChat() diff --git a/indra/newview/skins/default/xui/en/menu_place_add_button.xml b/indra/newview/skins/default/xui/en/menu_place_add_button.xml new file mode 100644 index 0000000000..e3a39a1242 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_place_add_button.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu + layout="topleft" + left="0" + mouse_opaque="false" + name="menu_folder_gear" + visible="false"> +    <menu_item_call +     label="Add Folder" +     layout="topleft" +     name="add_folder"> +        <on_click +         function="Places.LandmarksGear.Add.Action" +         parameter="category" /> +        <on_enable +         function="Places.LandmarksGear.Enable" +         parameter="category" /> +    </menu_item_call> +    <menu_item_call +     label="Add Landmark" +     layout="topleft" +     name="add_landmark"> +        <on_click +         function="Places.LandmarksGear.Add.Action" +         parameter="add_landmark" /> +    </menu_item_call> +</menu> diff --git a/indra/newview/skins/default/xui/en/panel_group_control_panel.xml b/indra/newview/skins/default/xui/en/panel_group_control_panel.xml index 9767a673f6..148e9a5562 100644 --- a/indra/newview/skins/default/xui/en/panel_group_control_panel.xml +++ b/indra/newview/skins/default/xui/en/panel_group_control_panel.xml @@ -7,12 +7,13 @@      <avatar_list       color="DkGray2"       follows="left|top|right|bottom" -     height="150" +     height="130" +     ignore_online_status="true"       layout="topleft"       left="3"       name="speakers_list"       top="10" -     width="140"/> +     width="140" />      <button       name="group_info_btn"       label="Group Info" @@ -24,4 +25,10 @@       label="Call"       width="90"       height="20" /> +    <button +     name="end_call_btn" +     label="End Call" +     width="90" +     height="20"  +     visible="false"/>  </panel> diff --git a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml b/indra/newview/skins/default/xui/en/panel_im_control_panel.xml index 7dc94d1141..dca52def49 100644 --- a/indra/newview/skins/default/xui/en/panel_im_control_panel.xml +++ b/indra/newview/skins/default/xui/en/panel_im_control_panel.xml @@ -1,7 +1,7 @@  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>  <panel name="panel_im_control_panel"         width="96" -       height="215" +       height="225"         border="false">    <avatar_icon name="avatar_icon" @@ -24,6 +24,13 @@            width="90"            height="20" /> +    <button +     height="20" +     label="End Call" +     name="end_call_btn" +     visible="false" +     width="90" /> +    <button name="share_btn"            label="Share"            width="90" diff --git a/indra/newview/skins/default/xui/en/panel_landmarks.xml b/indra/newview/skins/default/xui/en/panel_landmarks.xml index c33f68eaf7..5293043ba7 100644 --- a/indra/newview/skins/default/xui/en/panel_landmarks.xml +++ b/indra/newview/skins/default/xui/en/panel_landmarks.xml @@ -114,22 +114,10 @@           image_disabled="AddItem_Disabled"           layout="topleft"           left_pad="5" -         name="add_landmark_btn" +         name="add_btn"           picture_style="true"           tool_tip="Add new landmark"           width="18" /> -        <button -         follows="bottom|left" -         height="18" -         image_selected="AddItem_Press" -         image_unselected="AddItem_Off" -         image_disabled="AddItem_Disabled" -         layout="topleft" -         left_pad="5" -         name="add_folder_btn" -         picture_style="true" -         tool_tip="Add new folder" -         width="18" />          <dnd_button           follows="bottom|right"           height="18" | 
