diff --git a/resources/images/images.properties b/resources/images/images.properties index f314a616e..f5e0c4bf5 100644 --- a/resources/images/images.properties +++ b/resources/images/images.properties @@ -57,6 +57,7 @@ TEXT_ITALIC_ROLLOVER_BUTTON=resources/images/impl/gui/buttons/textItalicRollover TEXT_UNDERLINED_ROLLOVER_BUTTON=resources/images/impl/gui/buttons/textUnderlinedRollover.png DIAL_BUTTON=resources/images/impl/gui/buttons/dialButton.png HOLD_BUTTON=resources/images/impl/gui/buttons/holdButton.png +MUTE_BUTTON=resources/images/impl/gui/buttons/muteButton.png INVITE_DIALOG_ICON=resources/images/impl/gui/common/inviteDialogIcon.png SEND_SMS_ICON=resources/images/impl/gui/common/gsm.png DIAL_BUTTON_BG=resources/images/impl/gui/buttons/dialButtonBg.png diff --git a/resources/images/impl/gui/buttons/muteButton.png b/resources/images/impl/gui/buttons/muteButton.png new file mode 100644 index 000000000..75ae9249f Binary files /dev/null and b/resources/images/impl/gui/buttons/muteButton.png differ diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java index 0f50f2105..b7da4f726 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java @@ -82,6 +82,10 @@ public CallParticipantPanel(CallManager callManager, Component holdButton = new HoldButton(this.callParticipant); holdButton.setBounds(0, 74, 36, 36); contactPanel.add(holdButton, new Integer(1)); + + Component muteButton = new MuteButton(this.callParticipant); + muteButton.setBounds(36, 74, 36, 36); + contactPanel.add(muteButton, new Integer(1)); dialButton = new DialButton(callManager, new ImageIcon(ImageLoader.getImage(ImageLoader.DIAL_BUTTON))); diff --git a/src/net/java/sip/communicator/impl/gui/main/call/MuteButton.java b/src/net/java/sip/communicator/impl/gui/main/call/MuteButton.java new file mode 100644 index 000000000..4e3a138f2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/call/MuteButton.java @@ -0,0 +1,119 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.gui.main.call; + +import java.awt.event.*; + +import javax.swing.*; + +import net.java.sip.communicator.impl.gui.utils.*; +import net.java.sip.communicator.service.protocol.*; + +/** + * Represents an UI means to mute the audio stream sent to an associated + * CallPariticant. + * + * @author Lubomir Marinov + */ +public class MuteButton + extends JToggleButton +{ + + /** + * Initializes a new MuteButton instance which is to mute the audio + * stream to a specific CallParticipant. + * + * @param callParticipant the CallParticipant to be associated with + * the new instance and to have the audio stream sent to muted + */ + public MuteButton(CallParticipant callParticipant) + { + super(new ImageIcon(ImageLoader.getImage(ImageLoader.MUTE_BUTTON))); + + setModel(new MuteButtonModel(callParticipant)); + } + + /** + * Represents the model of a toggle button that mutes the audio stream sent + * to a specific CallParticipant. + */ + private static class MuteButtonModel + extends ToggleButtonModel + { + + /** + * The CallParticipant whose state is being adapted for the + * purposes of depicting as a toggle button. + */ + private final CallParticipant callParticipant; + + /** + * Initializes a new MuteButtonModel instance to represent the + * state of a specific CallParticipant as a toggle button. + * + * @param callParticipant the CallParticipant whose state is to + * be represented as a toggle button + */ + public MuteButtonModel(CallParticipant callParticipant) + { + this.callParticipant = callParticipant; + + addActionListener(new ActionListener() + { + + /** + * Invoked when an action occurs. + * + * @param evt the ActionEvent instance containing the + * data associated with the action and the act of its + * performing + */ + public void actionPerformed(ActionEvent evt) + { + MuteButtonModel.this.actionPerformed(this, evt); + } + }); + } + + /** + * Handles actions performed on this model on behalf of a specific + * ActionListener. + * + * @param listener the ActionListener notified about the + * performing of the action + * @param evt the ActionEvent containing the data associated + * with the action and the act of its performing + */ + private void actionPerformed(ActionListener listener, ActionEvent evt) + { + Call call = callParticipant.getCall(); + + if (call != null) + { + OperationSetBasicTelephony telephony = + (OperationSetBasicTelephony) call.getProtocolProvider() + .getOperationSet(OperationSetBasicTelephony.class); + + telephony.setMute(callParticipant, !callParticipant.isMute()); + + fireItemStateChanged(new ItemEvent(this, + ItemEvent.ITEM_STATE_CHANGED, this, + isSelected() ? ItemEvent.SELECTED : ItemEvent.DESELECTED)); + fireStateChanged(); + } + } + + /** + * Determines whether this model represents a state which should be + * visualized by the currently depicting toggle button as selected. + */ + public boolean isSelected() + { + return callParticipant.isMute(); + } + } +} diff --git a/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java b/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java index ee406c52f..185a7a866 100644 --- a/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java +++ b/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java @@ -403,6 +403,11 @@ public class ImageLoader { public static final ImageID HOLD_BUTTON = new ImageID("HOLD_BUTTON"); + /** + * A mute button icon. The icon shown in the CallParticipant panel. + */ + public static final ImageID MUTE_BUTTON = new ImageID("MUTE_BUTTON"); + /** * The image used, when a contact has no photo specified. */ diff --git a/src/net/java/sip/communicator/impl/gui/utils/images.properties b/src/net/java/sip/communicator/impl/gui/utils/images.properties index dee63341d..928796a91 100644 --- a/src/net/java/sip/communicator/impl/gui/utils/images.properties +++ b/src/net/java/sip/communicator/impl/gui/utils/images.properties @@ -58,6 +58,7 @@ TEXT_ITALIC_ROLLOVER_BUTTON=resources/images/impl/gui/buttons/textItalicRollover TEXT_UNDERLINED_ROLLOVER_BUTTON=resources/images/impl/gui/buttons/textUnderlinedRollover.png DIAL_BUTTON=resources/images/impl/gui/buttons/dialButton.png HOLD_BUTTON=resources/images/impl/gui/buttons/holdButton.png +MUTE_BUTTON=resources/images/impl/gui/buttons/muteButton.png INVITE_DIALOG_ICON=resources/images/impl/gui/common/inviteDialogIcon.png SEND_SMS_ICON=resources/images/impl/gui/common/gsm.png DIAL_BUTTON_BG=resources/images/impl/gui/buttons/dialButtonBg.png diff --git a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java index ff28481c6..de7b45ece 100644 --- a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java +++ b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java @@ -2370,4 +2370,25 @@ public void run() } } + /** + * Determines whether the audio of this session is (set to) mute. + * + * @return true if the audio of this session is (set to) mute; + * otherwise, false + */ + public boolean isMute() + { + return mediaServCallback.getMediaControl(getCall()).isMute(); + } + + /** + * Sets the mute state of the audio of this session. + * + * @param mute true to mute the audio of this session; otherwise, + * false + */ + public void setMute(boolean mute) + { + mediaServCallback.getMediaControl(getCall()).setMute(mute); + } } diff --git a/src/net/java/sip/communicator/impl/media/MediaControl.java b/src/net/java/sip/communicator/impl/media/MediaControl.java index 54f81297f..1c22680ae 100644 --- a/src/net/java/sip/communicator/impl/media/MediaControl.java +++ b/src/net/java/sip/communicator/impl/media/MediaControl.java @@ -6,22 +6,20 @@ */ package net.java.sip.communicator.impl.media; +import java.awt.Dimension; import java.io.*; import java.net.*; +import java.util.*; import javax.media.*; +import javax.media.control.*; +import javax.media.format.*; import javax.media.protocol.*; +import javax.sdp.*; import net.java.sip.communicator.impl.media.device.*; -import net.java.sip.communicator.util.*; -import javax.sdp.*; -import net.java.sip.communicator.service.media.MediaException; -import java.util.*; import net.java.sip.communicator.service.configuration.*; -import javax.media.control.*; -import javax.media.format.*; -import javax.media.rtp.*; -import java.awt.Dimension; - +import net.java.sip.communicator.service.media.MediaException; +import net.java.sip.communicator.util.*; /** * This class is intended to provide a generic way to control media package. @@ -32,6 +30,7 @@ * @author Jean Lorchat * @author Ryan Ricard * @author Ken Larson + * @author Lubomir Marinov */ public class MediaControl { @@ -57,6 +56,11 @@ public class MediaControl */ private DataSource avDataSource = null; + /** + * The audio DataSource which provides mute support. + */ + private MutePushBufferDataSource muteAudioDataSource; + /** * SDP Codes of all video formats that JMF supports. */ @@ -172,8 +176,8 @@ public class MediaControl */ public MediaControl() { - } + /** * Returns the duration of the output data source. Usually this will be * DURATION_UNKNOWN, but if the current data source is set to an audio @@ -372,6 +376,13 @@ public void initCaptureDevices() { audioDataSource = createDataSource(audioDeviceInfo.getLocator()); audioCaptureDevice = (CaptureDevice) audioDataSource; + + /* Provide mute support for the audio (if possible). */ + if (audioDataSource instanceof PushBufferDataSource) + audioDataSource = + muteAudioDataSource = + new MutePushBufferDataSource( + (PushBufferDataSource) audioDataSource); } // video device @@ -1198,4 +1209,26 @@ private void registerCustomPackages() + currentPackagePrefix); } + /** + * Determines whether the audio of this instance is mute. + * + * @return true if the audio of this instance is mute; otherwise, + * false + */ + public boolean isMute() + { + return (muteAudioDataSource != null) && muteAudioDataSource.isMute(); + } + + /** + * Sets the mute state of the audio of this instance. + * + * @param mute true to mute the audio of this instance; + * false, otherwise + */ + public void setMute(boolean mute) + { + if (muteAudioDataSource != null) + muteAudioDataSource.setMute(mute); + } } diff --git a/src/net/java/sip/communicator/impl/media/MutePushBufferDataSource.java b/src/net/java/sip/communicator/impl/media/MutePushBufferDataSource.java new file mode 100644 index 000000000..fa14d42e3 --- /dev/null +++ b/src/net/java/sip/communicator/impl/media/MutePushBufferDataSource.java @@ -0,0 +1,275 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.media; + +import java.io.*; +import java.util.*; +import javax.media.*; +import javax.media.control.*; +import javax.media.protocol.*; + +/** + * Implements a PushBufferDataSource wrapper which provides mute + * support for the wrapped instance. + *

+ * Because the class wouldn't work for our use case without it, + * CaptureDevice is implemented and is being delegated to the wrapped + * DataSource (if it supports the interface in question). + *

+ * + * @author Lubomir Marinov + */ +public class MutePushBufferDataSource + extends PushBufferDataSource + implements CaptureDevice +{ + + /** + * The wrapped DataSource this instance provides mute support for. + */ + private final PushBufferDataSource dataSource; + + /** + * The indicator which determines whether this DataSource is mute. + */ + private boolean mute; + + /** + * Initializes a new MutePushBufferDataSource instance which is to + * provide mute support for a specific PushBufferDataSource. + * + * @param dataSource the PushBufferDataSource the new instance is + * to provide mute support for + */ + public MutePushBufferDataSource(PushBufferDataSource dataSource) + { + this.dataSource = dataSource; + } + + public void connect() throws IOException + { + dataSource.connect(); + } + + public void disconnect() + { + dataSource.disconnect(); + } + + public CaptureDeviceInfo getCaptureDeviceInfo() + { + CaptureDeviceInfo captureDeviceInfo; + + if (dataSource instanceof CaptureDevice) + captureDeviceInfo = + ((CaptureDevice) dataSource).getCaptureDeviceInfo(); + else + captureDeviceInfo = null; + return captureDeviceInfo; + } + + public String getContentType() + { + return dataSource.getContentType(); + } + + public Object getControl(String controlType) + { + return dataSource.getControl(controlType); + } + + public Object[] getControls() + { + return dataSource.getControls(); + } + + public Time getDuration() + { + return dataSource.getDuration(); + } + + public FormatControl[] getFormatControls() + { + FormatControl[] formatControls; + + if (dataSource instanceof CaptureDevice) + formatControls = ((CaptureDevice) dataSource).getFormatControls(); + else + formatControls = new FormatControl[0]; + return formatControls; + } + + public PushBufferStream[] getStreams() + { + PushBufferStream[] streams = dataSource.getStreams(); + + if (streams != null) + for (int streamIndex = 0; streamIndex < streams.length; streamIndex++) + streams[streamIndex] = + new MutePushBufferStream(streams[streamIndex]); + return streams; + } + + /** + * Determines whether this DataSource is mute. + * + * @return true if this DataSource is mute; otherwise, + * false + */ + public synchronized boolean isMute() + { + return mute; + } + + /** + * Sets the mute state of this DataSource. + * + * @param mute true to mute this DataSource; otherwise, + * false + */ + public synchronized void setMute(boolean mute) + { + this.mute = mute; + } + + public void start() throws IOException + { + dataSource.start(); + } + + public void stop() throws IOException + { + dataSource.stop(); + } + + /** + * Implements a PushBufferStream wrapper which provides mute + * support for the wrapped instance. + */ + private class MutePushBufferStream + implements PushBufferStream + { + + /** + * The wrapped stream this instance provides mute support for. + */ + private final PushBufferStream stream; + + /** + * Initializes a new MutePushBufferStream instance which is to + * provide mute support for a specific PushBufferStream. + * + * @param stream the PushBufferStream the new instance is to + * provide mute support for + */ + public MutePushBufferStream(PushBufferStream stream) + { + this.stream = stream; + } + + public ContentDescriptor getContentDescriptor() + { + return stream.getContentDescriptor(); + } + + public long getContentLength() + { + return stream.getContentLength(); + } + + public Object getControl(String controlType) + { + return stream.getControl(controlType); + } + + public Object[] getControls() + { + return stream.getControls(); + } + + public Format getFormat() + { + return stream.getFormat(); + } + + public boolean endOfStream() + { + return stream.endOfStream(); + } + + public void read(Buffer buffer) throws IOException + { + stream.read(buffer); + + if (isMute()) + { + Object data = buffer.getData(); + + if (data != null) + { + Class dataClass = data.getClass(); + final int fromIndex = buffer.getOffset(); + final int toIndex = fromIndex + buffer.getLength(); + + if (Format.byteArray.equals(dataClass)) + Arrays + .fill((byte[]) data, fromIndex, toIndex, (byte) 0); + else if (Format.intArray.equals(dataClass)) + Arrays.fill((int[]) data, fromIndex, toIndex, 0); + else if (Format.shortArray.equals(dataClass)) + Arrays.fill((short[]) data, fromIndex, toIndex, + (short) 0); + + buffer.setData(data); + } + } + } + + public void setTransferHandler(BufferTransferHandler transferHandler) + { + stream.setTransferHandler((transferHandler == null) ? null + : new MuteBufferTransferHandler(transferHandler)); + } + + /** + * Implements a BufferTransferHandler wrapper which doesn't + * expose a wrapped PushBufferStream but rather its wrapper in + * order to give full control to the + * {@link PushBufferStream#read(Buffer)} method of the wrapper. + */ + public class MuteBufferTransferHandler + implements BufferTransferHandler + { + + /** + * The wrapped BufferTransferHandler which receives the + * actual events from the wrapped PushBufferStream. + */ + private final BufferTransferHandler transferHandler; + + /** + * Initializes a new MuteBufferTransferHandler instance + * which is to overwrite the source PushBufferStream of a + * specific BufferTransferHandler. + * + * @param transferHandler the BufferTransferHandler the new + * instance is to overwrite the source + * PushBufferStream of + */ + public MuteBufferTransferHandler( + BufferTransferHandler transferHandler) + { + this.transferHandler = transferHandler; + } + + public void transferData(PushBufferStream stream) + { + transferHandler.transferData(MutePushBufferStream.this); + } + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java index 8c14f800b..6a61ff53b 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java @@ -30,8 +30,8 @@ * @author Symphorien Wanko */ public class OperationSetBasicTelephonyJabberImpl - implements OperationSetBasicTelephony, - RegistrationStateChangeListener, + extends AbstractOperationSetBasicTelephony + implements RegistrationStateChangeListener, JingleMediaListener, JingleTransportListener, JingleSessionRequestListener, @@ -52,11 +52,6 @@ public class OperationSetBasicTelephonyJabberImpl */ private ProtocolProviderServiceJabberImpl protocolProvider = null; - /** - * A list of listeners registered for call events. - */ - private Vector callListeners = new Vector(); - /** * Contains references for all currently active (non ended) calls. */ @@ -134,21 +129,6 @@ else if ((evt.getNewState() == RegistrationState.UNREGISTERED)) } } - /** - * Registers listener with this provider so that it - * could be notified when incoming calls are received. - * - * @param listener the listener to register with this provider. - */ - public void addCallListener(CallListener listener) - { - synchronized(callListeners) - { - if (!callListeners.contains(listener)) - callListeners.add(listener); - } - } - /** * Create a new call and invite the specified CallParticipant to it. * @@ -295,40 +275,6 @@ private CallJabberImpl createOutgoingCall(String calleeAddress) return (CallJabberImpl) callParticipant.getCall(); } - /** - * Creates and dispatches a CallEvent notifying registered - * listeners that an event with id eventID has occurred on - * sourceCall. - * - * @param eventID the ID of the event to dispatch - * @param sourceCall the call on which the event has occurred. - */ - protected void fireCallEvent( int eventID, - CallJabberImpl sourceCall) - { - CallEvent cEvent = new CallEvent(sourceCall, eventID); - - logger.debug("Dispatching a CallEvent to " - + callListeners.size() - + " listeners. event is: " + cEvent.toString()); - Iterator listeners = null; - synchronized(callListeners) - { - listeners = new ArrayList(callListeners).iterator(); - } - - while(listeners.hasNext()) - { - CallListener listener = (CallListener)listeners.next(); - if(eventID == CallEvent.CALL_INITIATED) - listener.outgoingCallCreated(cEvent); - else if(eventID == CallEvent.CALL_RECEIVED) - listener.incomingCallReceived(cEvent); - else if(eventID == CallEvent.CALL_ENDED) - listener.callEnded(cEvent); - } - } - /** * Returns an iterator over all currently active calls. * @@ -363,19 +309,6 @@ public void putOnHold(CallParticipant participant) getJingleMediaSession().setTrasmit(false); } - /** - * Removes the listener from the list of call listeners. - * - * @param listener the listener to unregister. - */ - public void removeCallListener(CallListener listener) - { - synchronized(callListeners) - { - callListeners.remove(listener); - } - } - /** * Implements method hangupCallParticipant * from OperationSetBasicTelephony. diff --git a/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java b/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java index 43a270210..96b37d308 100644 --- a/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java +++ b/src/net/java/sip/communicator/impl/protocol/mock/MockOperationSetBasicTelephony.java @@ -13,18 +13,12 @@ * @author Damian Minkov */ public class MockOperationSetBasicTelephony - implements OperationSetBasicTelephony, - CallChangeListener + extends AbstractOperationSetBasicTelephony + implements CallChangeListener { private static final Logger logger = Logger.getLogger(MockOperationSetBasicTelephony.class); - /** - * A list of listeners registered for - * CallEvents. - */ - private Vector callListeners = new Vector(); - /** * A reference to the ProtocolProviderServiceSipImpl instance * that created us. @@ -42,19 +36,6 @@ public MockOperationSetBasicTelephony(MockProvider protocolProvider) this.protocolProvider = protocolProvider; } - /** - * Registers the specified CallListener with this provider so that it - * could be notified when incoming calls are received. - * - * @param listener the listener to register with this provider. - */ - public void addCallListener(CallListener listener) - { - synchronized(callListeners){ - callListeners.add(listener); - } - } - /** * Indicates a user request to answer an incoming call from the specified * CallParticipant. @@ -193,18 +174,6 @@ public void putOnHold(CallParticipant participant) throws { } - /** - * Removes the specified listener from the list of call listeners. - * - * @param listener the listener to unregister. - */ - public void removeCallListener(CallListener listener) - { - synchronized(callListeners){ - callListeners.remove(listener); - } - } - public Call receiveCall(String fromAddress) throws Exception { @@ -230,38 +199,6 @@ public Call placeCall(String toAddress) return newCall; } - /** - * Creates and dispatches a CallEvent notifying registered - * listeners that an event with id eventID has occurred on - * sourceCall. - * - * @param eventID the ID of the event to dispatch - * @param sourceCall the call on which the event has occurred. - */ - protected void fireCallEvent( int eventID, - Call sourceCall) - { - CallEvent cEvent = new CallEvent(sourceCall, eventID); - - logger.debug("Dispatching a CallEvent to " - + callListeners.size() - +" listeners. event is: " + cEvent.toString()); - - Iterator listeners = new ArrayList(callListeners).iterator(); - - while(listeners.hasNext()) - { - CallListener listener = (CallListener)listeners.next(); - - if(eventID == CallEvent.CALL_INITIATED) - listener.outgoingCallCreated(cEvent); - else if(eventID == CallEvent.CALL_RECEIVED) - listener.incomingCallReceived(cEvent); - else if(eventID == CallEvent.CALL_ENDED) - listener.callEnded(cEvent); - } - } - public CallParticipant addNewCallParticipant(Call call, String address) { MockCallParticipant callPArt = new MockCallParticipant(address, (MockCall)call); diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallParticipantSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallParticipantSipImpl.java index 73b08d1f8..87b9a7a59 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallParticipantSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallParticipantSipImpl.java @@ -10,6 +10,7 @@ import java.util.*; import javax.sip.address.*; +import net.java.sip.communicator.service.media.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; @@ -488,4 +489,25 @@ public void setCallInfoURL(URL callControlURL) { this.callControlURL = callControlURL; } + + /** + * Determines whether the audio stream (if any) being sent to this + * participant is mute. + * + * @return true if an audio stream is being sent to this + * participant and it is currently mute; false, otherwise + */ + public boolean isMute() + { + CallSipImpl call = this.call; + + if (call != null) + { + CallSession callSession = call.getMediaCallSession(); + + if (callSession != null) + return callSession.isMute(); + } + return false; + } } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java index 6eca61e99..f4b85a494 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java @@ -27,8 +27,8 @@ * @author Lubomir Marinov */ public class OperationSetBasicTelephonySipImpl - implements OperationSetBasicTelephony - , SipListener + extends AbstractOperationSetBasicTelephony + implements SipListener { private static final Logger logger = Logger.getLogger(OperationSetBasicTelephonySipImpl.class); @@ -39,11 +39,6 @@ public class OperationSetBasicTelephonySipImpl */ private ProtocolProviderServiceSipImpl protocolProvider = null; - /** - * A liste of listeners registered for call events. - */ - private Vector callListeners = new Vector(); - /** * Contains references for all currently active (non ended) calls. */ @@ -79,21 +74,6 @@ public OperationSetBasicTelephonySipImpl( protocolProvider.registerMethodProcessor(Request.BYE, this); } - /** - * Registers listener with this provider so that it - * could be notified when incoming calls are received. - * - * @param listener the listener to register with this provider. - */ - public void addCallListener(CallListener listener) - { - synchronized(callListeners) - { - if (!callListeners.contains(listener)) - callListeners.add(listener); - } - } - /** * Create a new call and invite the specified CallParticipant to it. * @@ -308,42 +288,6 @@ private CallSipImpl createOutgoingCall(Address calleeAddress) return (CallSipImpl)callParticipant.getCall(); } - /** - * Creates and dispatches a CallEvent notifying registered - * listeners that an event with id eventID has occurred on - * sourceCall. - * - * @param eventID the ID of the event to dispatch - * @param sourceCall the call on which the event has occurred. - */ - protected void fireCallEvent( int eventID, - CallSipImpl sourceCall) - { - CallEvent cEvent = new CallEvent(sourceCall, eventID); - - logger.debug("Dispatching a CallEvent to " - + callListeners.size() - +" listeners. event is: " + cEvent.toString()); - - Iterator listeners = null; - synchronized(callListeners) - { - listeners = new ArrayList(callListeners).iterator(); - } - - while(listeners.hasNext()) - { - CallListener listener = (CallListener)listeners.next(); - - if(eventID == CallEvent.CALL_INITIATED) - listener.outgoingCallCreated(cEvent); - else if(eventID == CallEvent.CALL_RECEIVED) - listener.incomingCallReceived(cEvent); - else if(eventID == CallEvent.CALL_ENDED) - listener.callEnded(cEvent); - } - } - /** * Returns an iterator over all currently active calls. * @@ -534,19 +478,6 @@ private void throwOperationFailedException(String message, int errorCode, throw new OperationFailedException(message, errorCode, cause); } - /** - * Removes the listener from the list of call listeners. - * - * @param listener the listener to unregister. - */ - public void removeCallListener(CallListener listener) - { - synchronized(callListeners) - { - callListeners.remove(listener); - } - } - /** * Processes a Request received on a SipProvider upon which this SipListener * is registered. @@ -2456,4 +2387,22 @@ public synchronized void shutdown() } } } + + /** + * Sets the mute state of the audio stream being sent to a specific + * CallParticipant. + *

+ * The implementation sends silence through the audio stream. + *

+ * + * @param participant the CallParticipant who receives the audio + * stream to have its mute state set + * @param mute true to mute the audio stream being sent to + * participant; otherwise, false + */ + public void setMute(CallParticipant participant, boolean mute) + { + ((CallSipImpl) participant.getCall()).getMediaCallSession().setMute( + mute); + } } diff --git a/src/net/java/sip/communicator/service/media/CallSession.java b/src/net/java/sip/communicator/service/media/CallSession.java index b83bd889f..b38852156 100644 --- a/src/net/java/sip/communicator/service/media/CallSession.java +++ b/src/net/java/sip/communicator/service/media/CallSession.java @@ -168,4 +168,20 @@ public void processSdpAnswer(CallParticipant responder, String sdpAnswer) * URL is available. */ public URL getCallInfoURL(); + + /** + * Determines whether the audio of this session is (set to) mute. + * + * @return true if the audio of this session is (set to) mute; + * otherwise, false + */ + public boolean isMute(); + + /** + * Sets the mute state of the audio of this session. + * + * @param mute true to mute the audio of this session; otherwise, + * false + */ + public void setMute(boolean mute); } diff --git a/src/net/java/sip/communicator/service/protocol/AbstractCallParticipant.java b/src/net/java/sip/communicator/service/protocol/AbstractCallParticipant.java index d38ab5cbc..b59d7e9c1 100644 --- a/src/net/java/sip/communicator/service/protocol/AbstractCallParticipant.java +++ b/src/net/java/sip/communicator/service/protocol/AbstractCallParticipant.java @@ -17,7 +17,6 @@ * The DefaultCallParticipant provides a default implementation for most of the * CallParticpant methods with the purpose of only leaving custom protocol * development to clients using the PhoneUI service. - *

* * @author Emil Ivov */ @@ -160,4 +159,19 @@ public URL getCallInfoURL() //they should override this method return null; } + + /** + * Determines whether the audio stream (if any) being sent to this + * participant is mute. + *

+ * The default implementation returns false. + *

+ * + * @return true if an audio stream is being sent to this + * participant and it is currently mute; false, otherwise + */ + public boolean isMute() + { + return false; + } } diff --git a/src/net/java/sip/communicator/service/protocol/AbstractOperationSetBasicTelephony.java b/src/net/java/sip/communicator/service/protocol/AbstractOperationSetBasicTelephony.java new file mode 100644 index 000000000..810a0ed3d --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/AbstractOperationSetBasicTelephony.java @@ -0,0 +1,115 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.service.protocol; + +import java.util.*; + +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; + +/** + * Represents a default implementation of OperationSetBasicTelephony in + * order to make it easier for implementers to provide complete solutions while + * focusing on implementation-specific details. + * + * @author Lubomir Marinov + */ +public abstract class AbstractOperationSetBasicTelephony + implements OperationSetBasicTelephony +{ + private static final Logger logger = + Logger.getLogger(AbstractOperationSetBasicTelephony.class); + + /** + * A list of listeners registered for call events. + */ + private final List callListeners = new Vector(); + + /** + * Registers listener with this provider so that it + * could be notified when incoming calls are received. + * + * @param listener the listener to register with this provider. + */ + public void addCallListener(CallListener listener) + { + synchronized(callListeners) + { + if (!callListeners.contains(listener)) + callListeners.add(listener); + } + } + + /** + * Creates and dispatches a CallEvent notifying registered + * listeners that an event with id eventID has occurred on + * sourceCall. + * + * @param eventID the ID of the event to dispatch + * @param sourceCall the call on which the event has occurred. + */ + public void fireCallEvent(int eventID, Call sourceCall) + { + CallEvent cEvent = new CallEvent(sourceCall, eventID); + + logger.debug("Dispatching a CallEvent to " + callListeners.size() + + " listeners. event is: " + cEvent.toString()); + + Iterator listeners = null; + synchronized (callListeners) + { + listeners = new ArrayList(callListeners).iterator(); + } + + while (listeners.hasNext()) + { + CallListener listener = (CallListener) listeners.next(); + + if (eventID == CallEvent.CALL_INITIATED) + listener.outgoingCallCreated(cEvent); + else if (eventID == CallEvent.CALL_RECEIVED) + listener.incomingCallReceived(cEvent); + else if (eventID == CallEvent.CALL_ENDED) + listener.callEnded(cEvent); + } + } + + /** + * Removes the listener from the list of call listeners. + * + * @param listener the listener to unregister. + */ + public void removeCallListener(CallListener listener) + { + synchronized(callListeners) + { + callListeners.remove(listener); + } + } + + /** + * Sets the mute state of the audio stream being sent to a specific + * CallParticipant. + *

+ * The default implementation does nothing. + *

+ * + * @param participant the CallParticipant who receives the audio + * stream to have its mute state set + * @param mute true to mute the audio stream being sent to + * participant; otherwise, false + */ + public void setMute(CallParticipant participant, boolean mute) + { + + /* + * While throwing UnsupportedOperationException may be a possible + * approach, putOnHold/putOffHold just do nothing when not supported so + * this implementation takes inspiration from them. + */ + } +} diff --git a/src/net/java/sip/communicator/service/protocol/CallParticipant.java b/src/net/java/sip/communicator/service/protocol/CallParticipant.java index 0c18065cd..21b01e2c2 100644 --- a/src/net/java/sip/communicator/service/protocol/CallParticipant.java +++ b/src/net/java/sip/communicator/service/protocol/CallParticipant.java @@ -132,4 +132,13 @@ public interface CallParticipant * is available. */ public URL getCallInfoURL(); + + /** + * Determines whether the audio stream (if any) being sent to this + * participant is mute. + * + * @return true if an audio stream is being sent to this + * participant and it is currently mute; false, otherwise + */ + public boolean isMute(); } diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java b/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java index 34c4ff763..c8bba6b1d 100644 --- a/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java +++ b/src/net/java/sip/communicator/service/protocol/OperationSetBasicTelephony.java @@ -14,9 +14,10 @@ * An Operation Set defining all basic telephony operations such as conducting * simple calls and etc. Note that video is not considered as a part of a * supplementary operation set and if included in the service should be available - * behind the basic telephoy set. + * behind the basic telephony set. * * @author Emil Ivov + * @author Lubomir Marinov */ public interface OperationSetBasicTelephony extends OperationSet @@ -74,7 +75,7 @@ public Call createCall(Contact callee) /** * Indicates a user request to answer an incoming call from the specified * CallParticipant. - * @param participant the call participant that we'd like to anwer. + * @param participant the call participant that we'd like to answer. * @throws OperationFailedException with the corresponding code if we * encounter an error while performing this operation. */ @@ -106,7 +107,7 @@ public void putOffHold(CallParticipant participant) /** * Indicates a user request to end a call with the specified call - * particiapnt. + * participant. * @param participant the participant that we'd like to hang up on. * @throws OperationFailedException with the corresponding code if we * encounter an error while performing this operation. @@ -119,4 +120,19 @@ public void hangupCallParticipant(CallParticipant participant) * @return Iterator */ public Iterator getActiveCalls(); + + /** + * Sets the mute state of the audio stream being sent to a specific + * CallParticipant. + *

+ * Muting an audio stream is implementation specific and one of the possible + * approaches to it is sending silence. + *

+ * + * @param participant the CallParticipant who receives the audio + * stream to have its mute state set + * @param mute true to mute the audio stream being sent to + * participant; otherwise, false + */ + public void setMute(CallParticipant participant, boolean mute); }