diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 0e1d5ca80b5e97447d5ceb8d7098c142fe11f684..0bfc0db351d41ac6c22b6eca9a779f82dea61f32 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -386,6 +386,7 @@ set(viewer_SOURCE_FILES
     llspatialpartition.cpp
     llspeakbutton.cpp
     llspeakers.cpp
+    llspeakingindicatormanager.cpp
     llsplitbutton.cpp
     llsprite.cpp
     llstartup.cpp
@@ -891,6 +892,7 @@ set(viewer_HEADER_FILES
     llspatialpartition.h
     llspeakbutton.h
     llspeakers.h
+    llspeakingindicatormanager.h
     llsplitbutton.h
     llsprite.h
     llstartup.h
@@ -1759,6 +1761,7 @@ if (INSTALL)
 endif (INSTALL)
 
 if (LL_TESTS)
+else (LL_TESTS)
   # To add a viewer unit test, just add the test .cpp file below
   # This creates a separate test project per file listed.
   include(LLAddBuildTest)
diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp
index a2e17db3d8ad451f01c145ecc555c7593f70e717..1043858373cd77d270dc561f4b2afb58ab48c723 100644
--- a/indra/newview/llavatarlistitem.cpp
+++ b/indra/newview/llavatarlistitem.cpp
@@ -91,40 +91,25 @@ BOOL  LLAvatarListItem::postBuild()
 	mProfileBtn->setVisible(false);
 	mProfileBtn->setClickedCallback(boost::bind(&LLAvatarListItem::onProfileBtnClick, this));
 
-	// Remember avatar icon width including its padding from the name text box,
-	// so that we can hide and show the icon again later.
 	if (!sStaticInitialized)
 	{
+		// Remember children widths including their padding from the next sibling,
+		// so that we can hide and show them again later.
 		initChildrenWidths(this);
 
 		sStaticInitialized = true;
 	}
 
-/*
-	if(!p.buttons.profile)
-	{
-		delete mProfile;
-		mProfile = NULL;
-
-		LLRect rect;
-
-		rect.setLeftTopAndSize(mName->getRect().mLeft, mName->getRect().mTop, mName->getRect().getWidth() + 30, mName->getRect().getHeight());
-		mName->setRect(rect);
-
-		if(mLocator)
-		{
-			rect.setLeftTopAndSize(mLocator->getRect().mLeft + 30, mLocator->getRect().mTop, mLocator->getRect().getWidth(), mLocator->getRect().getHeight());
-			mLocator->setRect(rect);
-		}
+	return TRUE;
+}
 
-		if(mInfo)
-		{
-			rect.setLeftTopAndSize(mInfo->getRect().mLeft + 30, mInfo->getRect().mTop, mInfo->getRect().getWidth(), mInfo->getRect().getHeight());
-			mInfo->setRect(rect);
-		}
+S32 LLAvatarListItem::notifyParent(const LLSD& info)
+{
+	if (info.has("visibility_changed"))
+	{
+		updateChildren();
 	}
-*/
-	return TRUE;
+	return 0;
 }
 
 void LLAvatarListItem::onMouseEnter(S32 x, S32 y, MASK mask)
@@ -246,8 +231,10 @@ void LLAvatarListItem::showSpeakingIndicator(bool visible)
 	// Already done? Then do nothing.
 	if (mSpeakingIndicator->getVisible() == (BOOL)visible)
 		return;
-	mSpeakingIndicator->setVisible(visible);
-	updateChildren();
+// Disabled to not contradict with SpeakingIndicatorManager functionality. EXT-3976
+// probably this method should be totally removed.
+//	mSpeakingIndicator->setVisible(visible);
+//	updateChildren();
 }
 
 void LLAvatarListItem::setAvatarIconVisible(bool visible)
@@ -264,26 +251,6 @@ void LLAvatarListItem::setAvatarIconVisible(bool visible)
 void LLAvatarListItem::onInfoBtnClick()
 {
 	LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", mAvatarId));
-
-	/* TODO fix positioning of inspector
-	localPointToScreen(mXPos, mYPos, &mXPos, &mYPos);
-	
-	
-	LLRect rect;
-
-	// *TODO Vadim: rewrite this. "+= -" looks weird.
-	S32 delta = mYPos - inspector->getRect().getHeight();
-	if(delta < 0)
-	{
-		mYPos += -delta;
-	}
-	
-	rect.setLeftTopAndSize(mXPos, mYPos,
-	inspector->getRect().getWidth(), inspector->getRect().getHeight()); 
-	inspector->setRect(rect);
-	inspector->setFrontmost(true);
-	inspector->setVisible(true);
-	*/
 }
 
 void LLAvatarListItem::onProfileBtnClick()
diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h
index 868ee546d44be124d6eb2f524c23f58181b9beff..f76ffb391d455a9bd1f3c2bfbabe026976dacfce 100644
--- a/indra/newview/llavatarlistitem.h
+++ b/indra/newview/llavatarlistitem.h
@@ -74,6 +74,11 @@ class LLAvatarListItem : public LLPanel, public LLFriendObserver
 	virtual ~LLAvatarListItem();
 
 	virtual BOOL postBuild();
+
+	/**
+	 * Processes notification from speaker indicator to update children when indicator's visibility is changed.
+	 */
+	virtual S32	notifyParent(const LLSD& info);
 	virtual void onMouseLeave(S32 x, S32 y, MASK mask);
 	virtual void onMouseEnter(S32 x, S32 y, MASK mask);
 	virtual void setValue(const LLSD& value);
diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp
index 63803469dd1519e25dcfa9b686fc1eea81d5801e..f816dc589d68e0c6d73b81fc22986b32b6c6fd6d 100644
--- a/indra/newview/lloutputmonitorctrl.cpp
+++ b/indra/newview/lloutputmonitorctrl.cpp
@@ -77,7 +77,9 @@ LLOutputMonitorCtrl::LLOutputMonitorCtrl(const LLOutputMonitorCtrl::Params& p)
 	mImageLevel3(p.image_level_3),
 	mAutoUpdate(p.auto_update),
 	mSpeakerId(p.speaker_id),
-	mIsAgentControl(false)
+	mIsAgentControl(false),
+	mIsSwitchDirty(false),
+	mShouldSwitchOn(false)
 {
 	//static LLUIColor output_monitor_muted_color = LLUIColorTable::instance().getColor("OutputMonitorMutedColor", LLColor4::orange);
 	//static LLUIColor output_monitor_overdriven_color = LLUIColorTable::instance().getColor("OutputMonitorOverdrivenColor", LLColor4::red);
@@ -108,6 +110,7 @@ LLOutputMonitorCtrl::LLOutputMonitorCtrl(const LLOutputMonitorCtrl::Params& p)
 LLOutputMonitorCtrl::~LLOutputMonitorCtrl()
 {
 	LLMuteList::getInstance()->removeObserver(this);
+	LLSpeakingIndicatorManager::unregisterSpeakingIndicator(mSpeakerId, this);
 }
 
 void LLOutputMonitorCtrl::setPower(F32 val)
@@ -117,6 +120,26 @@ void LLOutputMonitorCtrl::setPower(F32 val)
 
 void LLOutputMonitorCtrl::draw()
 {
+	// see also switchIndicator()
+	if (mIsSwitchDirty)
+	{
+		mIsSwitchDirty = false;
+		if (mShouldSwitchOn)
+		{
+			// just notify parent visibility may have changed
+			notifyParentVisibilityChanged();
+		}
+		else
+		{
+			// make itself invisible and notify parent about this
+			setVisible(FALSE);
+			notifyParentVisibilityChanged();
+
+			// no needs to render for invisible element
+			return;
+		}
+	}
+
 	// Copied from llmediaremotectrl.cpp
 	// *TODO: Give the LLOutputMonitorCtrl an agent-id to monitor, then
 	// call directly into gVoiceClient to ask if that agent-id is muted, is
@@ -229,6 +252,7 @@ void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id)
 	if (speaker_id.isNull() || speaker_id == mSpeakerId) return;
 
 	mSpeakerId = speaker_id;
+	LLSpeakingIndicatorManager::registerSpeakingIndicator(mSpeakerId, this);
 
 	//mute management
 	if (mAutoUpdate)
@@ -251,3 +275,42 @@ void LLOutputMonitorCtrl::onChange()
 	// check only blocking on voice. EXT-3542
 	setIsMuted(LLMuteList::getInstance()->isMuted(mSpeakerId, LLMute::flagVoiceChat));
 }
+
+// virtual
+void LLOutputMonitorCtrl::switchIndicator(bool switch_on)
+{
+	// ensure indicator is visible in case it is not in visible chain
+	// to be called when parent became visible next time to notify parent that visibility is changed.
+	setVisible(TRUE);
+
+	// if parent is in visible chain apply switch_on state and notify it immediately
+	if (getParent() && getParent()->isInVisibleChain())
+	{
+		LL_DEBUGS("SpeakingIndicator") << "Indicator is in visible chain, notifying parent: " << mSpeakerId << LL_ENDL;
+		setVisible((BOOL)switch_on);
+		notifyParentVisibilityChanged();
+	}
+
+	// otherwise remember necessary state and mark itself as dirty.
+	// State will be applied i next draw when parents chain became visible.
+	else
+	{
+		LL_DEBUGS("SpeakingIndicator") << "Indicator is not in visible chain, parent won't be notified: " << mSpeakerId << LL_ENDL;
+		mIsSwitchDirty = true;
+		mShouldSwitchOn = switch_on;
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////
+// PRIVATE SECTION
+//////////////////////////////////////////////////////////////////////////
+void LLOutputMonitorCtrl::notifyParentVisibilityChanged()
+{
+	LL_DEBUGS("SpeakingIndicator") << "Notify parent that visibility was changed: " << mSpeakerId << " ,new_visibility: " << getVisible() << LL_ENDL;
+
+	LLSD params = LLSD().with("visibility_changed", getVisible());
+
+	notifyParent(params);
+}
+
+// EOF
diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h
index 85ea552a572b049fbf796b0f914fcabb9481f7d5..2bbfa251e9d8e2624b3e1bd1881ce951b0868495 100644
--- a/indra/newview/lloutputmonitorctrl.h
+++ b/indra/newview/lloutputmonitorctrl.h
@@ -36,6 +36,7 @@
 #include "v4color.h"
 #include "llview.h"
 #include "llmutelist.h"
+#include "llspeakingindicatormanager.h"
 
 class LLTextBox;
 class LLUICtrlFactory;
@@ -45,7 +46,7 @@ class LLUICtrlFactory;
 //
 
 class LLOutputMonitorCtrl
-: public LLView, LLMuteListObserver
+: public LLView, public LLSpeakingIndicator, LLMuteListObserver
 {
 public:
 	struct Params : public LLInitParam::Block<Params, LLView::Params>
@@ -90,7 +91,29 @@ class LLOutputMonitorCtrl
 	//called by mute list
 	virtual void onChange();
 
+	/**
+	 * Implementation of LLSpeakingIndicator interface.
+	 * Behavior is implemented via changing visibility.
+	 *
+	 * If instance is in visible chain now (all parents are visible) it changes visibility 
+	 * and notify parent about this.
+	 *
+	 * Otherwise it marks an instance as dirty and stores necessary visibility.
+	 * It will be applied in next draw and parent will be notified.
+	 */
+	virtual void	switchIndicator(bool switch_on);
+
 private:
+
+	/**
+	 * Notifies parent about changed visibility.
+	 *
+	 * Passes LLSD with "visibility_changed" => <current visibility> value.
+	 * For now it is processed by LLAvatarListItem to update (reshape) its children.
+	 * Implemented fo complete EXT-3976
+	 */
+	void			notifyParentVisibilityChanged();
+
 	//static LLColor4	sColorMuted;
 	//static LLColor4	sColorNormal;
 	//static LLColor4	sColorOverdriven;
@@ -117,6 +140,10 @@ class LLOutputMonitorCtrl
 
 	/** uuid of a speaker being monitored */
 	LLUUID			mSpeakerId;
+
+	/** indicates if the instance is dirty and should notify parent */
+	bool			mIsSwitchDirty;
+	bool			mShouldSwitchOn;
 };
 
 #endif
diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..42db6bf9c3f43373563385410a0f6911da7106e1
--- /dev/null
+++ b/indra/newview/llspeakingindicatormanager.cpp
@@ -0,0 +1,254 @@
+/** 
+ * @file llspeakingindicatormanager.cpp
+ * @author Mike Antipov
+ * @brief Implementation of SpeackerIndicatorManager class to process registered LLSpeackerIndicator
+ * depend on avatars are in the same voice channel.
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ * 
+ * Copyright (c) 2010, 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 "llspeakingindicatormanager.h"
+
+
+#include "llagentdata.h"
+#include "llvoicechannel.h"
+#include "llvoiceclient.h"
+
+/**
+ * This class intended to control visibility of avatar speaking indicators depend on whether avatars
+ * are in the same voice channel.
+ *
+ * Speaking indicator should be visible for avatars in the same voice channel. See EXT-3976.
+ *
+ * It stores passed instances of LLOutputMonitorCtrl in a multimap by avatar LLUUID.
+ * It observes changing of voice channel and changing of participant list in voice channel.
+ * When voice channel or voice participant list is changed it updates visibility of an appropriate 
+ * speaking indicator.
+ *
+ * Several indicators can be registered for the same avatar.
+ */
+class SpeakingIndicatorManager : public LLSingleton<SpeakingIndicatorManager>, LLVoiceClientParticipantObserver
+{
+	LOG_CLASS(SpeakingIndicatorManager);
+public:
+
+	/**
+	 * Stores passed speaking indicator to control its visibility.
+	 *
+	 * Registered indicator is set visible if an appropriate avatar is in the same voice channel with Agent.
+	 * It ignores instances of Agent's indicator.
+	 *
+	 * @param speaker_id LLUUID of an avatar whose speaking indicator is registered.
+	 * @param speaking_indicator instance of the speaking indicator to be registered.
+	 */
+	void registerSpeakingIndicator(const LLUUID& speaker_id, LLSpeakingIndicator* const speaking_indicator);
+
+	/**
+	 * Removes passed speaking indicator from observing.
+	 *
+	 * @param speaker_id LLUUID of an avatar whose speaking indicator should be unregistered.
+	 * @param speaking_indicator instance of the speaking indicator to be unregistered.
+	 */
+	void unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator);
+
+private:
+	typedef std::set<LLUUID> speaker_ids_t;
+	typedef std::multimap<LLUUID, LLSpeakingIndicator*> speaking_indicators_mmap_t;
+	typedef speaking_indicators_mmap_t::value_type speaking_indicator_value_t;
+	typedef speaking_indicators_mmap_t::const_iterator indicator_const_iterator;
+	typedef std::pair<indicator_const_iterator, indicator_const_iterator> indicator_range_t;
+
+	friend class LLSingleton<SpeakingIndicatorManager>;
+	SpeakingIndicatorManager();
+	~SpeakingIndicatorManager();
+
+	/**
+	 * Callback to determine when voice channel is changed.
+	 *
+	 * It switches all registered speaking indicators off.
+	 * To reduce overheads only switched on indicators are processed.
+	 */
+	void sOnCurrentChannelChanged(const LLUUID& session_id);
+
+	/**
+	 * Callback of changing voice participant list (from LLVoiceClientParticipantObserver).
+	 *
+	 * Switches off indicators had been switched on and switches on indicators of current participants list.
+	 * There is only a few indicators in lists should be switched off/on.
+	 * So, method does not calculate difference between these list it only switches off already 
+	 * switched on indicators and switches on indicators of voice channel participants
+	 */
+	void onChange();
+
+	/**
+	 * Changes state of indicators specified by LLUUIDs
+	 *
+	 * @param speakers_uuids - avatars' LLUUIDs whose speaking indicators should be switched
+	 * @param switch_on - if TRUE specified indicator will be switched on, off otherwise.
+	 */
+	void switchSpeakerIndicators(const speaker_ids_t& speakers_uuids, BOOL switch_on);
+
+	/**
+	 * Multimap with all registered speaking indicators
+	 */
+	speaking_indicators_mmap_t mSpeakingIndicators;
+
+	/**
+	 * LUUIDs of avatar for which we have speaking indicators switched on.
+	 *
+	 * Is used to switch off all previously ON indicators when voice participant list is changed.
+	 *
+	 * @see onChange()
+	 */
+	speaker_ids_t mSwitchedIndicatorsOn;
+};
+
+//////////////////////////////////////////////////////////////////////////
+// PUBLIC SECTION
+//////////////////////////////////////////////////////////////////////////
+void SpeakingIndicatorManager::registerSpeakingIndicator(const LLUUID& speaker_id, LLSpeakingIndicator* const speaking_indicator)
+{
+	if (speaker_id == gAgentID) return;
+
+	LL_DEBUGS("SpeakingIndicator") << "Registering indicator: " << speaker_id << LL_ENDL;
+	speaking_indicator_value_t value_type(speaker_id, speaking_indicator);
+	mSpeakingIndicators.insert(value_type);
+
+	speaker_ids_t speakers_uuids;
+	BOOL is_in_same_voice = LLVoiceClient::getInstance()->findParticipantByID(speaker_id) != NULL;
+
+	speakers_uuids.insert(speaker_id);
+	switchSpeakerIndicators(speakers_uuids, is_in_same_voice);
+}
+
+void SpeakingIndicatorManager::unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator)
+{
+	speaking_indicators_mmap_t::iterator it;
+	it = mSpeakingIndicators.find(speaker_id);
+	for (;it != mSpeakingIndicators.end(); ++it)
+	{
+		if (it->second == speaking_indicator)
+		{
+			mSpeakingIndicators.erase(it);
+			break;
+		}
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////
+// PRIVATE SECTION
+//////////////////////////////////////////////////////////////////////////
+SpeakingIndicatorManager::SpeakingIndicatorManager()
+{
+	LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&SpeakingIndicatorManager::sOnCurrentChannelChanged, this, _1));
+	LLVoiceClient::getInstance()->addObserver(this);
+}
+
+SpeakingIndicatorManager::~SpeakingIndicatorManager()
+{
+	// Don't use LLVoiceClient::getInstance() here without check
+	// singleton MAY have already been destroyed.
+	if(LLVoiceClient::instanceExists())
+	{
+		LLVoiceClient::getInstance()->removeObserver(this);
+	}
+}
+
+void SpeakingIndicatorManager::sOnCurrentChannelChanged(const LLUUID& /*session_id*/)
+{
+	switchSpeakerIndicators(mSwitchedIndicatorsOn, FALSE);
+	mSwitchedIndicatorsOn.clear();
+}
+
+void SpeakingIndicatorManager::onChange()
+{
+	LL_DEBUGS("SpeakingIndicator") << "Voice participant list was changed, updating indicators" << LL_ENDL;
+
+	speaker_ids_t speakers_uuids;
+	LLVoiceClient::getInstance()->getParticipantsUUIDSet(speakers_uuids);
+
+	LL_DEBUGS("SpeakingIndicator") << "Switching all OFF, count: " << mSwitchedIndicatorsOn.size() << LL_ENDL;
+	// switch all indicators off
+	switchSpeakerIndicators(mSwitchedIndicatorsOn, FALSE);
+	mSwitchedIndicatorsOn.clear();
+
+	LL_DEBUGS("SpeakingIndicator") << "Switching all ON, count: " << speakers_uuids.size() << LL_ENDL;
+	// then switch current voice participants indicators on
+	switchSpeakerIndicators(speakers_uuids, TRUE);
+}
+
+void SpeakingIndicatorManager::switchSpeakerIndicators(const speaker_ids_t& speakers_uuids, BOOL switch_on)
+{
+	speaker_ids_t::const_iterator it_uuid = speakers_uuids.begin(); 
+	for (; it_uuid != speakers_uuids.end(); ++it_uuid)
+	{
+		LL_DEBUGS("SpeakingIndicator") << "Looking for indicator: " << *it_uuid << LL_ENDL;
+		indicator_range_t it_range = mSpeakingIndicators.equal_range(*it_uuid);
+		indicator_const_iterator it_indicator = it_range.first;
+		bool was_found = false;
+		for (; it_indicator != it_range.second; ++it_indicator)
+		{
+			was_found = true;
+			LLSpeakingIndicator* indicator = (*it_indicator).second;
+			indicator->switchIndicator(switch_on);
+		}
+
+		if (was_found)
+		{
+			LL_DEBUGS("SpeakingIndicator") << mSpeakingIndicators.count(*it_uuid) << " indicators where found" << LL_ENDL;
+
+			if (switch_on)
+			{
+				// store switched on indicator to be able switch it off
+				mSwitchedIndicatorsOn.insert(*it_uuid);
+			}
+		}
+		else
+		{
+			LL_WARNS("SpeakingIndicator") << "indicator was not found among registered: " << *it_uuid << LL_ENDL;
+		}
+	}
+}
+
+/************************************************************************/
+/*         LLSpeakingIndicatorManager namespace implementation          */
+/************************************************************************/
+
+void LLSpeakingIndicatorManager::registerSpeakingIndicator(const LLUUID& speaker_id, LLSpeakingIndicator* const speaking_indicator)
+{
+	SpeakingIndicatorManager::instance().registerSpeakingIndicator(speaker_id, speaking_indicator);
+}
+
+void LLSpeakingIndicatorManager::unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator)
+{
+	SpeakingIndicatorManager::instance().unregisterSpeakingIndicator(speaker_id, speaking_indicator);
+}
+
+// EOF
+
diff --git a/indra/newview/llspeakingindicatormanager.h b/indra/newview/llspeakingindicatormanager.h
new file mode 100644
index 0000000000000000000000000000000000000000..ce0158f7d86d1adfec641a4131673f239dec5854
--- /dev/null
+++ b/indra/newview/llspeakingindicatormanager.h
@@ -0,0 +1,67 @@
+/** 
+ * @file llspeakingindicatormanager.h
+ * @author Mike Antipov
+ * @brief Interfeace of LLSpeackerIndicator class to be processed depend on avatars are in the same voice channel.
+ * Also register/unregister methods for this class are declared
+ *
+ * $LicenseInfo:firstyear=2010&license=viewergpl$
+ * 
+ * Copyright (c) 2010, 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_LLSPEAKINGINDICATORMANAGER_H
+#define LL_LLSPEAKINGINDICATORMANAGER_H
+
+class LLSpeakingIndicator
+{
+public:
+	virtual void switchIndicator(bool switch_on) = 0;
+};
+
+// See EXT-3976.
+namespace LLSpeakingIndicatorManager
+{
+	/**
+	 * Stores passed speaking indicator to control its visibility.
+	 *
+	 * Registered indicator is set visible if an appropriate avatar is in the same voice channel with Agent.
+	 * It ignores instances of Agent's indicator.
+	 *
+	 * @param speaker_id LLUUID of an avatar whose speaker indicator is registered.
+	 * @param speaking_indicator instance of the speaker indicator to be registered.
+	 */
+	void registerSpeakingIndicator(const LLUUID& speaker_id, LLSpeakingIndicator* const speaking_indicator);
+
+	/**
+	 * Removes passed speaking indicator from observing.
+	 *
+	 * @param speaker_id LLUUID of an avatar whose speaker indicator should be unregistered.
+	 * @param speaking_indicator instance of the speaker indicator to be unregistered.
+	 */
+	void unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator);
+}
+
+#endif // LL_LLSPEAKINGINDICATORMANAGER_H
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index 423c46e14c75ce3204840189e240a8ec078cd3de..42b8a1c2b6bee3ceca36d236e2a8f5ec69946f7b 100644
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -5004,6 +5004,17 @@ LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void)
 	return result;
 }
 
+void LLVoiceClient::getParticipantsUUIDSet(std::set<LLUUID>& participant_uuids)
+{
+	if (NULL == mAudioSession) return;
+
+	participantUUIDMap::const_iterator it = mAudioSession->mParticipantsByUUID.begin(),
+		it_end = mAudioSession->mParticipantsByUUID.end();
+	for (; it != it_end; ++it)
+	{
+		participant_uuids.insert((*(*it).first));
+	}
+}
 
 LLVoiceClient::participantState *LLVoiceClient::sessionState::findParticipant(const std::string &uri)
 {
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
index 724179847dc2dcb97271e0e5ccbe3d660660a0e5..6231c6ba29f47b3bb92090a2d93ba986779bd32a 100644
--- a/indra/newview/llvoiceclient.h
+++ b/indra/newview/llvoiceclient.h
@@ -354,6 +354,7 @@ static	void updatePosition(void);
 
 		participantState *findParticipantByID(const LLUUID& id);
 		participantMap *getParticipantList(void);
+		void getParticipantsUUIDSet(std::set<LLUUID>& participant_uuids);
 		
 		typedef std::map<const std::string*, sessionState*, stringMapComparitor> sessionMap;
 		typedef std::set<sessionState*> sessionSet;