diff --git a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallConference.java b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallConference.java
index 673ed83b0..f439dd5cf 100644
--- a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallConference.java
+++ b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallConference.java
@@ -7,11 +7,13 @@
package net.java.sip.communicator.service.protocol.media;
import java.beans.*;
+import java.lang.ref.*;
import java.util.*;
import org.jitsi.service.neomedia.*;
import org.jitsi.service.neomedia.device.*;
import org.jitsi.util.*;
+import org.jitsi.util.event.*;
import net.java.sip.communicator.service.protocol.*;
@@ -25,6 +27,13 @@
public class MediaAwareCallConference
extends CallConference
{
+ /**
+ * The PropertyChangeListener which will listen to the
+ * MediaService about PropertyChangeEvents.
+ */
+ private static WeakPropertyChangeListener
+ mediaServicePropertyChangeListener;
+
/**
* The MediaDevices indexed by MediaType ordinal which are
* to be used by this telephony conference for media capture and/or
@@ -47,12 +56,12 @@ public class MediaAwareCallConference
*/
private final PropertyChangeListener propertyChangeListener
= new PropertyChangeListener()
- {
- public void propertyChange(PropertyChangeEvent event)
- {
- MediaAwareCallConference.this.propertyChange(event);
- }
- };
+ {
+ public void propertyChange(PropertyChangeEvent ev)
+ {
+ MediaAwareCallConference.this.propertyChange(ev);
+ }
+ };
/**
* The RTPTranslator which forwards video RTP and RTCP traffic
@@ -92,8 +101,49 @@ public MediaAwareCallConference(boolean jitsiVideoBridge)
* Listen to the MediaService in order to reflect changes in the user's
* selection with respect to the default media device.
*/
- ProtocolMediaActivator.getMediaService().addPropertyChangeListener(
- propertyChangeListener);
+ addMediaServicePropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * Adds a specific PropertyChangeListener to be notified about
+ * PropertyChangeEvents fired by the current MediaService
+ * implementation. The implementation adds a WeakReference to the
+ * specified listener because MediaAwareCallConference
+ * is unable to determine when the PropertyChangeListener is to be
+ * removed.
+ *
+ * @param listener the PropertyChangeListener to add
+ */
+ private static synchronized void addMediaServicePropertyChangeListener(
+ PropertyChangeListener listener)
+ {
+ if (mediaServicePropertyChangeListener == null)
+ {
+ final MediaService mediaService
+ = ProtocolMediaActivator.getMediaService();
+
+ if (mediaService != null)
+ {
+ mediaServicePropertyChangeListener
+ = new WeakPropertyChangeListener()
+ {
+ protected void addThisToNotifier()
+ {
+ mediaService.addPropertyChangeListener(this);
+ }
+
+ protected void removeThisFromNotifier()
+ {
+ mediaService.removePropertyChangeListener(this);
+ }
+ };
+ }
+ }
+ if (mediaServicePropertyChangeListener != null)
+ {
+ mediaServicePropertyChangeListener.addPropertyChangeListener(
+ listener);
+ }
}
/**
@@ -103,6 +153,7 @@ public MediaAwareCallConference(boolean jitsiVideoBridge)
* not being such, disposes of the mixers used by this instance when it was
* a conference focus
*/
+ @Override
protected void conferenceFocusChanged(boolean oldValue, boolean newValue)
{
/*
@@ -243,17 +294,17 @@ public RTPTranslator getRTPTranslator(MediaType mediaType)
* {@link MediaService#DEFAULT_DEVICE} which represents the user's choice
* with respect to the default audio device.
*
- * @param event a PropertyChangeEvent which specifies the name of
- * the property which had its value changed and the old and new values of
- * that property
+ * @param ev a PropertyChangeEvent which specifies the name of the
+ * property which had its value changed and the old and new values of that
+ * property
*/
- private void propertyChange(PropertyChangeEvent event)
+ private void propertyChange(PropertyChangeEvent ev)
{
- String propertyName = event.getPropertyName();
+ String propertyName = ev.getPropertyName();
if (MediaService.DEFAULT_DEVICE.equals(propertyName))
{
- Object source = event.getSource();
+ Object source = ev.getSource();
if (source instanceof MediaService)
{
@@ -332,4 +383,183 @@ void setDevice(MediaType mediaType, MediaDevice device)
oldValue, newValue);
}
}
+
+ /**
+ * Implements a PropertyChangeListener which weakly references and
+ * delegates to specific PropertyChangeListeners and automatically
+ * adds itself to and removes itself from a specific
+ * PropertyChangeNotifier depending on whether there are
+ * PropertyChangeListeners to delegate to. Thus enables listening
+ * to a PropertyChangeNotifier by invoking
+ * {@link PropertyChangeNotifier#addPropertyChangeListener(
+ * PropertyChangeListener)} without
+ * {@link PropertyChangeNotifier#removePropertyChangeListener(
+ * PropertyChangeListener)}.
+ */
+ private static class WeakPropertyChangeListener
+ implements PropertyChangeListener
+ {
+ /**
+ * The indicator which determines whether this
+ * PropertyChangeListener has been added to {@link #notifier}.
+ */
+ private boolean added = false;
+
+ /**
+ * The list of PropertyChangeListeners which are to be notified
+ * about PropertyChangeEvents fired by {@link #notifier}.
+ */
+ private final List> listeners
+ = new LinkedList>();
+
+ /**
+ * The PropertyChangeNotifier this instance is to listen to
+ * about PropertyChangeEvents which are to be forwarded to
+ * {@link #listeners}.
+ */
+ private final PropertyChangeNotifier notifier;
+
+ /**
+ * Initializes a new WeakPropertyChangeListener instance.
+ */
+ protected WeakPropertyChangeListener()
+ {
+ this(null);
+ }
+
+ /**
+ * Initializes a new WeakPropertyChangeListener instance which
+ * is to listen to a specific PropertyChangeNotifier.
+ *
+ * @param notifier the PropertyChangeNotifier the new instance
+ * is to listen to
+ */
+ public WeakPropertyChangeListener(PropertyChangeNotifier notifier)
+ {
+ this.notifier = notifier;
+ }
+
+ /**
+ * Adds a specific PropertyChangeListener to the list of
+ * PropertyChangeListeners to be notified about
+ * PropertyChangeEvents fired by the
+ * PropertyChangeNotifier associated with this instance.
+ *
+ * @param listener the PropertyChangeListener to add
+ */
+ public synchronized void addPropertyChangeListener(
+ PropertyChangeListener listener)
+ {
+ Iterator> i
+ = listeners.iterator();
+ boolean add = true;
+
+ while (i.hasNext())
+ {
+ PropertyChangeListener l = i.next().get();
+
+ if (l == null)
+ i.remove();
+ else if (l.equals(listener))
+ add = false;
+ }
+ if (add
+ && listeners.add(
+ new WeakReference(listener))
+ && !this.added)
+ {
+ addThisToNotifier();
+ this.added = true;
+ }
+ }
+
+ /**
+ * Adds this as a PropertyChangeListener to {@link #notifier}.
+ */
+ protected void addThisToNotifier()
+ {
+ if (notifier != null)
+ notifier.addPropertyChangeListener(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Notifies this instance about a PropertyChangeEvent fired by
+ * {@link #notifier}.
+ */
+ public void propertyChange(PropertyChangeEvent ev)
+ {
+ PropertyChangeListener[] ls;
+ int n;
+
+ synchronized (this)
+ {
+ Iterator> i
+ = listeners.iterator();
+
+ ls = new PropertyChangeListener[listeners.size()];
+ n = 0;
+ while (i.hasNext())
+ {
+ PropertyChangeListener l = i.next().get();
+
+ if (l == null)
+ i.remove();
+ else
+ ls[n++] = l;
+ }
+ if ((n == 0) && this.added)
+ {
+ removeThisFromNotifier();
+ this.added = false;
+ }
+ }
+
+ if (n != 0)
+ {
+ for (PropertyChangeListener l : ls)
+ l.propertyChange(ev);
+ }
+ }
+
+ /**
+ * Removes a specific PropertyChangeListener from the list of
+ * PropertyChangeListeners to be notified about
+ * PropertyChangeEvents fired by the
+ * PropertyChangeNotifier associated with this instance.
+ *
+ * @param listener the PropertyChangeListener to remove
+ */
+ @SuppressWarnings("unused")
+ public synchronized void removePropertyChangeListener(
+ PropertyChangeListener listener)
+ {
+ Iterator> i
+ = listeners.iterator();
+
+ while (i.hasNext())
+ {
+ PropertyChangeListener l = i.next().get();
+
+ if ((l == null) || l.equals(listener))
+ i.remove();
+ }
+ if (this.added && (listeners.size() == 0))
+ {
+ removeThisFromNotifier();
+ this.added = false;
+ }
+ }
+
+ /**
+ * Removes this as a PropertyChangeListener from
+ * {@link #notifier}.
+ */
+ protected void removeThisFromNotifier()
+ {
+ if (notifier != null)
+ notifier.removePropertyChangeListener(this);
+ }
+ }
}