Committing the on hold patch from Lubomir Marinov

cusax-fix
Emil Ivov 18 years ago
parent 65462bbd1f
commit a2491f9b12

@ -56,6 +56,7 @@ TEXT_BOLD_ROLLOVER_BUTTON=resources/images/impl/gui/buttons/textBoldRollover.png
TEXT_ITALIC_ROLLOVER_BUTTON=resources/images/impl/gui/buttons/textItalicRollover.png
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
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

@ -27,6 +27,7 @@
* (i.e. those that implement OperationSetBasicTelephony).
*
* @author Damian Minkov
* @author Lubomir Marinov
*/
public class CallHistoryServiceImpl
implements CallHistoryService,
@ -448,8 +449,12 @@ else if(state.equals(CallParticipantState._ALERTING_REMOTE_SIDE))
return CallParticipantState.ALERTING_REMOTE_SIDE;
else if(state.equals(CallParticipantState._CONNECTING))
return CallParticipantState.CONNECTING;
else if(state.equals(CallParticipantState._ON_HOLD))
return CallParticipantState.ON_HOLD;
else if(state.equals(CallParticipantState._ON_HOLD_LOCALLY))
return CallParticipantState.ON_HOLD_LOCALLY;
else if(state.equals(CallParticipantState._ON_HOLD_MUTUALLY))
return CallParticipantState.ON_HOLD_MUTUALLY;
else if(state.equals(CallParticipantState._ON_HOLD_REMOTELY))
return CallParticipantState.ON_HOLD_REMOTELY;
else if(state.equals(CallParticipantState._INITIATING_CALL))
return CallParticipantState.INITIATING_CALL;
else if(state.equals(CallParticipantState._INCOMING_CALL))
@ -852,7 +857,7 @@ private void handleParticipantAdded(CallParticipant callParticipant)
if(callRecord == null)
return;
callParticipant.addCallParticipantListener(new CallParticipantListener()
callParticipant.addCallParticipantListener(new CallParticipantAdapter()
{
public void participantStateChanged(CallParticipantChangeEvent evt)
{
@ -866,27 +871,21 @@ public void participantStateChanged(CallParticipantChangeEvent evt)
if(participantRecord == null)
return;
if(evt.getNewValue().equals(CallParticipantState.CONNECTED))
CallParticipantState newState =
(CallParticipantState) evt.getNewValue();
if (newState.equals(CallParticipantState.CONNECTED)
&& !CallParticipantState.isOnHold((CallParticipantState)
evt.getOldValue()))
participantRecord.setStartTime(new Date());
participantRecord.
setState((CallParticipantState)evt.getNewValue());
participantRecord.setState(newState);
//Disconnected / Busy
//Disconnected / Connecting - fail
//Disconnected / Connected
}
}
public void participantDisplayNameChanged(
CallParticipantChangeEvent evt){}
public void participantAddressChanged(
CallParticipantChangeEvent evt){}
public void participantImageChanged(
CallParticipantChangeEvent evt){}
public void participantTransportAddressChanged(
CallParticipantChangeEvent evt){}
});
Date startDate = new Date();
@ -919,7 +918,10 @@ private void handleParticipantRemoved(CallParticipant callParticipant,
if(!callParticipant.getState().equals(CallParticipantState.DISCONNECTED))
cpRecord.setState(callParticipant.getState());
if(cpRecord.getState().equals(CallParticipantState.CONNECTED))
CallParticipantState cpRecordState = cpRecord.getState();
if (cpRecordState.equals(CallParticipantState.CONNECTED)
|| CallParticipantState.isOnHold(cpRecordState))
{
cpRecord.setEndTime(new Date());
}

@ -13,7 +13,6 @@
import javax.swing.*;
import javax.swing.Timer;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
@ -24,6 +23,7 @@
* shows information about call participants, call duration, etc.
*
* @author Yana Stamcheva
* @author Lubomir Marinov
*/
public class CallPanel
extends JScrollPane
@ -295,42 +295,50 @@ public void participantStateChanged(CallParticipantChangeEvent evt)
participantPanel.setState(
sourceParticipant.getState().getStateString());
if(evt.getNewValue() == CallParticipantState.ALERTING_REMOTE_SIDE)
Object newState = evt.getNewValue();
if(newState == CallParticipantState.ALERTING_REMOTE_SIDE)
{
NotificationManager
.fireNotification(NotificationManager.OUTGOING_CALL);
}
else if(evt.getNewValue() == CallParticipantState.BUSY)
else if(newState == CallParticipantState.BUSY)
{
NotificationManager.stopSound(NotificationManager.OUTGOING_CALL);
NotificationManager.fireNotification(NotificationManager.BUSY_CALL);
}
else if(evt.getNewValue() == CallParticipantState.CONNECTED) {
//start the timer that takes care of refreshing the time label
NotificationManager.stopSound(NotificationManager.OUTGOING_CALL);
NotificationManager.stopSound(NotificationManager.INCOMING_CALL);
participantPanel.startCallTimer();
else if(newState == CallParticipantState.CONNECTED) {
if (!CallParticipantState.isOnHold((CallParticipantState)
evt.getOldValue()))
{
// start the timer that takes care of refreshing the time label
NotificationManager
.stopSound(NotificationManager.OUTGOING_CALL);
NotificationManager
.stopSound(NotificationManager.INCOMING_CALL);
participantPanel.startCallTimer();
}
}
else if(evt.getNewValue() == CallParticipantState.CONNECTING) {
else if(newState == CallParticipantState.CONNECTING) {
}
else if(evt.getNewValue() == CallParticipantState.DISCONNECTED) {
else if(newState == CallParticipantState.DISCONNECTED) {
//The call participant should be already removed from the call
//see callParticipantRemoved
}
else if(evt.getNewValue() == CallParticipantState.FAILED) {
else if(newState == CallParticipantState.FAILED) {
//The call participant should be already removed from the call
//see callParticipantRemoved
}
else if(evt.getNewValue() == CallParticipantState.INCOMING_CALL) {
else if(newState == CallParticipantState.INCOMING_CALL) {
}
else if(evt.getNewValue() == CallParticipantState.INITIATING_CALL) {
else if(newState == CallParticipantState.INITIATING_CALL) {
}
else if(evt.getNewValue() == CallParticipantState.ON_HOLD) {
else if(CallParticipantState.isOnHold((CallParticipantState) newState)) {
}
else if(evt.getNewValue() == CallParticipantState.UNKNOWN) {
else if(newState == CallParticipantState.UNKNOWN) {
}
}

@ -23,6 +23,7 @@
* name, photo, call duration, etc.
*
* @author Yana Stamcheva
* @author Lubomir Marinov
*/
public class CallParticipantPanel
extends JPanel
@ -77,13 +78,17 @@ public CallParticipantPanel(CallManager callManager,
this.callParticipant = callParticipant;
this.stateLabel.setText(callParticipant.getState().getStateString());
Component holdButton = new HoldButton(this.callParticipant);
holdButton.setBounds(0, 74, 36, 36);
contactPanel.add(holdButton, new Integer(1));
dialButton = new DialButton(callManager,
new ImageIcon(ImageLoader.getImage(ImageLoader.DIAL_BUTTON)));
dialButton.setBounds(94, 74, 36, 36);
contactPanel.add(dialButton, new Integer(1));
contactPanel.add(dialButton, new Integer(2));
}
/**

@ -0,0 +1,184 @@
/*
* 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.*;
import net.java.sip.communicator.service.protocol.event.*;
/**
* Represents an UI means to put an associated <tt>CallPariticant</tt> on/off
* hold.
*
* @author Lubomir Marinov
*/
public class HoldButton
extends JToggleButton
{
/**
* Initializes a new <tt>HoldButton</tt> instance which is to put a specific
* <tt>CallParticipant</tt> on/off hold.
*
* @param callParticipant the <tt>CallParticipant</tt> to be associated with
* the new instance and to be put on/off hold upon performing its
* action
*/
public HoldButton(CallParticipant callParticipant)
{
super(new ImageIcon(ImageLoader.getImage(ImageLoader.HOLD_BUTTON)));
setModel(new HoldButtonModel(callParticipant));
}
/**
* Represents the model of a toggle button that puts an associated
* <tt>CallParticipant</tt> on/off hold.
*/
private static class HoldButtonModel
extends ToggleButtonModel
{
/**
* The <tt>CallParticipant</tt> whose state is being adapted for the
* purposes of depicting as a toggle button.
*/
private final CallParticipant callParticipant;
/**
* Initializes a new <tt>HoldButtonModel</tt> instance to represent
* the state of a specific <tt>CallParticipant</tt> as a toggle
* button.
*
* @param callParticipant
* the <tt>CallParticipant</tt> whose state is to be
* represented as a toggle button
*/
public HoldButtonModel(CallParticipant callParticipant)
{
this.callParticipant = callParticipant;
InternalListener listener = new InternalListener();
this.callParticipant.addCallParticipantListener(listener);
addActionListener(listener);
}
/**
* Handles actions performed on this model on behalf of a specific
* <tt>ActionListener</tt>.
*
* @param listener the <tt>ActionListener</tt> notified about the
* performing of the action
* @param evt the <tt>ActionEvent</tt> 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);
try
{
if (isSelected())
telephony.putOffHold(callParticipant);
else
telephony.putOnHold(callParticipant);
}
catch (OperationFailedException ex)
{
// TODO Auto-generated method stub
}
}
}
/**
* Determines whether this model represents a state which should be
* visualized by the currently depicting toggle button as selected.
*/
public boolean isSelected()
{
CallParticipantState state = callParticipant.getState();
return CallParticipantState.ON_HOLD_LOCALLY.equals(state)
|| CallParticipantState.ON_HOLD_MUTUALLY.equals(state);
}
/**
* Handles changes in the state of the source <tt>CallParticipant</tt>
* on behalf of a specific <tt>CallParticipantListener</tt>.
*
* @param listener the <tt>CallParticipantListener</tt> notified about
* the state change
* @param evt the <tt>CallParticipantChangeEvent</tt> containing the
* source event as well as its previous and its new state
*/
private void participantStateChanged(CallParticipantListener listener,
CallParticipantChangeEvent evt)
{
CallParticipantState newState =
(CallParticipantState) evt.getNewValue();
CallParticipant callParticipant = evt.getSourceCallParticipant();
fireItemStateChanged(new ItemEvent(this,
ItemEvent.ITEM_STATE_CHANGED, this,
isSelected() ? ItemEvent.SELECTED : ItemEvent.DESELECTED));
fireStateChanged();
/*
* For the sake of completeness, try to not leave a listener on the
* CallParticipant after it's no longer of interest.
*/
if (CallParticipantState.DISCONNECTED.equals(newState))
{
callParticipant.removeCallParticipantListener(listener);
}
}
/**
* Represents the set of <tt>EventListener</tt>s this instance uses
* to track the changes in its <tt>CallParticipant</tt> model.
*/
private class InternalListener
extends CallParticipantAdapter
implements ActionListener
{
/**
* Invoked when an action occurs.
*
* @param evt the <tt>ActionEvent</tt> instance containing the data
* associated with the action and the act of its
* performing
*/
public void actionPerformed(ActionEvent evt)
{
HoldButtonModel.this.actionPerformed(this, evt);
}
/**
* Indicates that a change has occurred in the state of the source
* CallParticipant.
*
* @param evt The <tt>CallParticipantChangeEvent</tt> instance
* containing the source event as well as its previous
* and its new status.
*/
public void participantStateChanged(CallParticipantChangeEvent evt)
{
HoldButtonModel.this.participantStateChanged(this, evt);
}
}
}
}

@ -23,6 +23,7 @@
* Stores and loads images used throughout this ui implementation.
*
* @author Yana Stamcheva
* @author Lubomir Marinov
*/
public class ImageLoader {
@ -395,6 +396,13 @@ public class ImageLoader {
public static final ImageID DIAL_BUTTON
= new ImageID("DIAL_BUTTON");
/**
* A put-on/off-hold button icon. The icon shown in the CallParticipant
* panel.
*/
public static final ImageID HOLD_BUTTON
= new ImageID("HOLD_BUTTON");
/**
* The image used, when a contact has no photo specified.
*/

@ -57,6 +57,7 @@ TEXT_BOLD_ROLLOVER_BUTTON=resources/images/impl/gui/buttons/textBoldRollover.png
TEXT_ITALIC_ROLLOVER_BUTTON=resources/images/impl/gui/buttons/textItalicRollover.png
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
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

@ -62,6 +62,7 @@
* @author Ryan Ricard
* @author Ken Larson
* @author Dudek Przemyslaw
* @author Lubomir Marinov
*/
public class CallSessionImpl
implements CallSession
@ -159,6 +160,24 @@ public class CallSessionImpl
*/
private URL callURL = null;
/**
* The flag which signals that this side of the call has put the other on
* hold.
*/
private static final byte ON_HOLD_LOCALLY = 1 << 1;
/**
* The flag which signals that the other side of the call has put this on
* hold.
*/
private static final byte ON_HOLD_REMOTELY = 1 << 2;
/**
* The flags which determine whether this side of the call has put the other
* on hold and whether the other side of the call has put this on hold.
*/
private byte onHold;
/**
* List of RTP format strings which are supported by SIP Communicator in addition
* to the JMF standard formats.
@ -519,6 +538,318 @@ public String createSdpOffer(InetAddress intendedDestination)
return createSessionDescription(null, intendedDestination).toString();
}
/**
* The method is meant for use by protocol service implementations when
* willing to send an in-dialog invitation to a remote callee to put her
* on/off hold or to send an answer to an offer to be put on/off hold.
*
* @param participantSdpDescription the last SDP description of the remote
* callee
* @param on <tt>true</tt> if the SDP description should offer the remote
* callee to be put on hold or answer an offer from the remote
* callee to be put on hold; <tt>false</tt> to work in the
* context of a put-off-hold offer
* @return an SDP description <tt>String</tt> which offers the remote
* callee to be put her on/off hold or answers an offer from the
* remote callee to be put on/off hold
* @throws MediaException
*/
public String createSdpDescriptionForHold(String participantSdpDescription,
boolean on) throws MediaException
{
SessionDescription participantDescription = null;
System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!+"+participantSdpDescription);
try
{
participantDescription =
mediaServCallback.getSdpFactory().createSessionDescription(
participantSdpDescription);
}
catch (SdpParseException ex)
{
throwMediaException(
"Failed to parse the SDP description of the participant.",
MediaException.INTERNAL_ERROR, ex);
}
SessionDescription sdpOffer =
createSessionDescription(participantDescription, null);
Vector mediaDescriptions = null;
try
{
mediaDescriptions = sdpOffer.getMediaDescriptions(true);
}
catch (SdpException ex)
{
throwMediaException(
"Failed to get media descriptions from SDP offer.",
MediaException.INTERNAL_ERROR, ex);
}
for (Iterator mediaDescriptionIter = mediaDescriptions.iterator(); mediaDescriptionIter
.hasNext();)
{
MediaDescription mediaDescription =
(MediaDescription) mediaDescriptionIter.next();
Vector attributes = mediaDescription.getAttributes(false);
try
{
modifyMediaDescriptionForHold(on, mediaDescription, attributes);
}
catch (SdpException ex)
{
throwMediaException(
"Failed to modify media description for hold.",
MediaException.INTERNAL_ERROR, ex);
}
}
try
{
sdpOffer.setMediaDescriptions(mediaDescriptions);
}
catch (SdpException ex)
{
throwMediaException(
"Failed to set media descriptions to SDP offer.",
MediaException.INTERNAL_ERROR, ex);
}
return sdpOffer.toString();
}
/**
* Modifies the attributes of a specific <tt>MediaDescription</tt> in
* order to make them reflect the state of being on/off hold.
*
* @param on <tt>true</tt> if the state described by the modified
* <tt>MediaDescription</tt> should reflect being put on hold;
* <tt>false</tt> for being put off hold
* @param mediaDescription the <tt>MediaDescription</tt> to modify the
* attributes of
* @param attributes the attributes of <tt>mediaDescription</tt>
* @throws SdpException
*/
private void modifyMediaDescriptionForHold(boolean on,
MediaDescription mediaDescription, Vector attributes)
throws SdpException
{
/*
* The SDP offer to be put on hold represents a transition between
* sendrecv and sendonly or between recvonly and inactive depending on
* the current state.
*/
String oldAttribute = on ? "recvonly" : "inactive";
String newAttribute = null;
if (attributes != null)
for (Iterator attributeIter = attributes.iterator(); attributeIter
.hasNext();)
{
String attribute = ((Attribute) attributeIter.next()).getName();
if (oldAttribute.equalsIgnoreCase(attribute))
newAttribute = on ? "inactive" : "recvonly";
}
if (newAttribute == null)
newAttribute = on ? "sendonly" : "sendrecv";
mediaDescription.removeAttribute("inactive");
mediaDescription.removeAttribute("recvonly");
mediaDescription.removeAttribute("sendonly");
mediaDescription.removeAttribute("sendrecv");
mediaDescription.setAttribute(newAttribute, null);
}
/**
* Logs a specific message and associated <tt>Throwable</tt> cause as an
* error using the current <tt>Logger</tt> and then throws a new
* <tt>MediaException</tt> with the message, a specific error code and the
* cause.
*
* @param message the message to be logged and then wrapped in a new
* <tt>MediaException</tt>
* @param errorCode the error code to be assigned to the new
* <tt>MediaException</tt>
* @param cause the <tt>Throwable</tt> that has caused the necessity to
* log an error and have a new <tt>MediaException</tt> thrown
* @throws MediaException
*/
private void throwMediaException(String message, int errorCode,
Throwable cause) throws MediaException
{
logger.error(message, cause);
throw new MediaException(message, errorCode, cause);
}
/**
* Determines whether a specific SDP description <tt>String</tt> offers
* this party to be put on hold.
*
* @param sdpOffer the SDP description <tt>String</tt> to be examined for
* an offer to this party to be put on hold
* @return <tt>true</tt> if the specified SDP description <tt>String</tt>
* offers this party to be put on hold; <tt>false</tt>, otherwise
* @throws MediaException
*/
public boolean isSdpOfferToHold(String sdpOffer) throws MediaException
{
SessionDescription description = null;
try
{
description =
mediaServCallback.getSdpFactory().createSessionDescription(
sdpOffer);
}
catch (SdpParseException ex)
{
throwMediaException("Failed to parse SDP offer.",
MediaException.INTERNAL_ERROR, ex);
}
Vector mediaDescriptions = null;
try
{
mediaDescriptions = description.getMediaDescriptions(true);
}
catch (SdpException ex)
{
throwMediaException(
"Failed to get media descriptions from SDP offer.",
MediaException.INTERNAL_ERROR, ex);
}
boolean isOfferToHold = true;
for (Iterator mediaDescriptionIter = mediaDescriptions.iterator(); mediaDescriptionIter
.hasNext()
&& isOfferToHold;)
{
MediaDescription mediaDescription =
(MediaDescription) mediaDescriptionIter.next();
Vector attributes = mediaDescription.getAttributes(false);
isOfferToHold = false;
if (attributes != null)
{
for (Iterator attributeIter = attributes.iterator(); attributeIter
.hasNext()
&& !isOfferToHold;)
{
try
{
String attribute =
((Attribute) attributeIter.next()).getName();
if ("sendonly".equalsIgnoreCase(attribute)
|| "inactive".equalsIgnoreCase(attribute))
{
isOfferToHold = true;
}
}
catch (SdpParseException ex)
{
throwMediaException(
"Failed to get SDP media description attribute name",
MediaException.INTERNAL_ERROR, ex);
}
}
}
}
return isOfferToHold;
}
/**
* Puts the media of this <tt>CallSession</tt> on/off hold depending on
* the origin of the request.
* <p>
* For example, a remote request to have this party put off hold cannot
* override an earlier local request to put the remote party on hold.
* </p>
*
* @param on <tt>true</tt> to request the media of this
* <tt>CallSession</tt> be put on hold; <tt>false</tt>,
* otherwise
* @param here <tt>true</tt> if the request comes from this side of the
* call; <tt>false</tt> if the remote party is the issuer of
* the request i.e. it's the result of a remote offer
*/
public void putOnHold(boolean on, boolean here)
{
if (on)
{
onHold |= (here ? ON_HOLD_LOCALLY : ON_HOLD_REMOTELY);
}
else
{
onHold &= ~ (here ? ON_HOLD_LOCALLY : ON_HOLD_REMOTELY);
}
/* Put the send on/off hold. */
boolean sendOnHold =
(0 != (onHold & (ON_HOLD_LOCALLY | ON_HOLD_REMOTELY)));
putOnHold(getAudioRtpManager(), sendOnHold);
putOnHold(getVideoRtpManager(), sendOnHold);
/* Put the receive on/off hold. */
boolean receiveOnHold = (0 != (onHold & ON_HOLD_LOCALLY));
for (Iterator playerIter = players.iterator(); playerIter.hasNext();)
{
Player player = (Player) playerIter.next();
if (receiveOnHold)
player.stop();
else
player.start();
}
}
/**
* Puts a the <tt>SendSteam</tt>s of a specific <tt>RTPManager</tt>
* on/off hold i.e. stops/starts them.
*
* @param rtpManager the <tt>RTPManager</tt> to have its
* <tt>SendStream</tt>s on/off hold i.e. stopped/started
* @param on <tt>true</tt> to have the <tt>SendStream</tt>s of
* <tt>rtpManager</tt> put on hold i.e. stopped; <tt>false</tt>,
* otherwise
*/
private void putOnHold(RTPManager rtpManager, boolean on)
{
for (Iterator sendStreamIter = rtpManager.getSendStreams().iterator(); sendStreamIter
.hasNext();)
{
SendStream sendStream = (SendStream) sendStreamIter.next();
if (on)
{
try
{
sendStream.getDataSource().stop();
sendStream.stop();
}
catch (IOException ex)
{
logger.warn("Failed to stop SendStream.", ex);
}
}
else
{
try
{
sendStream.getDataSource().start();
sendStream.start();
}
catch (IOException ex)
{
logger.warn("Failed to start SendStream.", ex);
}
}
}
}
/**
* The method is meant for use by protocol service implementations upon
* reception of an SDP answer in response to an offer sent by us earlier.
@ -916,7 +1247,15 @@ private SessionDescription createSessionDescription(
}
}
allocateMediaPorts(intendedDestination);
/*
* For example, issuing a Request.INVITE for putting a
* CallParticipant on hold also needs a SessionDescrption. However,
* it just wants to describe the current state.
*/
if ((audioSessionAddress == null) || (videoSessionAddress == null))
{
allocateMediaPorts(intendedDestination);
}
InetAddress publicIpAddress = audioPublicAddress.getAddress();
@ -1087,12 +1426,15 @@ private Vector createMediaDescriptions(
, 1
, "RTP/AVP"
, supportedAudioEncodings);
byte onHold = this.onHold;
if (!mediaServCallback.getDeviceConfiguration()
.isAudioCaptureSupported())
{
am.setAttribute("recvonly", null);
/* We don't have anything to send. */
onHold |= ON_HOLD_REMOTELY;
}
setAttributeOnHold(am, onHold);
mediaDescs.add(am);
}
//--------Video media description
@ -1106,12 +1448,15 @@ private Vector createMediaDescriptions(
, 1
, "RTP/AVP"
, supportedVideoEncodings);
byte onHold = this.onHold;
if (!mediaServCallback.getDeviceConfiguration()
.isVideoCaptureSupported())
{
vm.setAttribute("recvonly", null);
/* We don't have anything to send. */
onHold |= ON_HOLD_REMOTELY;
}
setAttributeOnHold(vm, onHold);
mediaDescs.add(vm);
}
@ -1123,6 +1468,36 @@ private Vector createMediaDescriptions(
return mediaDescs;
}
/**
* Sets the call-hold related attribute of a specific
* <tt>MediaDescription</tt> to a specific value depending on the type of
* hold this <tt>CallSession</tt> is currently in.
*
* @param mediaDescription the <tt>MediaDescription</tt> to set the
* call-hold related attribute of
* @param onHold the call-hold state of this <tt>CallSession</tt> which is
* a combination of {@link #ON_HOLD_LOCALLY} and
* {@link #ON_HOLD_REMOTELY}
* @throws SdpException
*/
private void setAttributeOnHold(MediaDescription mediaDescription,
byte onHold) throws SdpException
{
String attribute;
if (ON_HOLD_LOCALLY == (onHold | ON_HOLD_LOCALLY))
attribute =
(ON_HOLD_REMOTELY == (onHold | ON_HOLD_REMOTELY)) ? "inactive"
: "sendonly";
else
attribute =
(ON_HOLD_REMOTELY == (onHold | ON_HOLD_REMOTELY)) ? "recvonly"
: null;
if (attribute != null)
mediaDescription.setAttribute(attribute, null);
}
/**
* Compares audio/video encodings in the <tt>offeredEncodings</tt>
* hashtable with those supported by the currently valid media controller

@ -24,6 +24,7 @@
* implementing OperationSetBasicTelephony.
*
* @author Emil Ivov
* @author Lubomir Marinov
*/
public class OperationSetBasicTelephonySipImpl
implements OperationSetBasicTelephony
@ -355,22 +356,182 @@ public Iterator getActiveCalls()
/**
* Resumes communication with a call participant previously put on hold.
*
*
* @param participant the call participant to put on hold.
* @throws OperationFailedException
*/
public synchronized void putOffHold(CallParticipant participant)
throws OperationFailedException
{
/** @todo implement putOffHold() */
putOnHold(participant, false);
}
/**
* Puts the specified CallParticipant "on hold".
*
*
* @param participant the participant that we'd like to put on hold.
* @throws OperationFailedException
*/
public synchronized void putOnHold(CallParticipant participant)
throws OperationFailedException
{
putOnHold(participant, true);
}
/**
* Puts the specified <tt>CallParticipant</tt> on or off hold.
*
* @param participant the <tt>CallParticipant</tt> to be put on or off hold
* @param on <tt>true</tt> to have the specified <tt>CallParticipant</tt>
* put on hold; <tt>false</tt>, otherwise
* @throws OperationFailedException
*/
private void putOnHold(CallParticipant participant, boolean on)
throws OperationFailedException
{
CallSession callSession =
((CallSipImpl) participant.getCall()).getMediaCallSession();
CallParticipantSipImpl sipParticipant =
(CallParticipantSipImpl) participant;
try
{
sendInviteRequest(sipParticipant, callSession
.createSdpDescriptionForHold(
sipParticipant.getSdpDescription(), on));
}
catch (MediaException ex)
{
throwOperationFailedException(
"Failed to create SDP offer to hold.",
OperationFailedException.INTERNAL_ERROR, ex);
}
/*
* Putting on hold isn't a negotiation (i.e. the issuing side takes the
* decision and executes it) so we're muting now regardless of the
* desire of the participant to accept the offer.
*/
callSession.putOnHold(on, true);
CallParticipantState state = sipParticipant.getState();
if (CallParticipantState.ON_HOLD_LOCALLY.equals(state))
{
if (!on)
sipParticipant.setState(CallParticipantState.CONNECTED);
}
else if (CallParticipantState.ON_HOLD_MUTUALLY.equals(state))
{
if (!on)
sipParticipant.setState(CallParticipantState.ON_HOLD_REMOTELY);
}
else if (CallParticipantState.ON_HOLD_REMOTELY.equals(state))
{
if (on)
sipParticipant.setState(CallParticipantState.ON_HOLD_MUTUALLY);
}
else if (on)
{
sipParticipant.setState(CallParticipantState.ON_HOLD_LOCALLY);
}
}
/**
* Sends an invite request with a specific SDP offer (description) within
* the current <tt>Dialog</tt> with a specific call participant.
*
* @param sipParticipant the SIP-specific call participant to send the
* invite to within the current <tt>Dialog</tt>
* @param sdpOffer the description of the SDP offer to be made to the
* specified call participant with the sent invite
* @throws OperationFailedException
*/
private void sendInviteRequest(CallParticipantSipImpl sipParticipant,
String sdpOffer) throws OperationFailedException
{
Dialog dialog = sipParticipant.getDialog();
Request invite = null;
try
{
invite = dialog.createRequest(Request.INVITE);
}
catch (SipException ex)
{
throwOperationFailedException("Failed to create invite request.",
OperationFailedException.INTERNAL_ERROR, ex);
}
/*
* The authorization-related headers are the responsibility of the
* application (according to the Javadoc of JAIN-SIP).
*/
AuthorizationHeader authorization =
protocolProvider.getSipSecurityManager()
.getCachedAuthorizationHeader(
((CallIdHeader) invite.getHeader(CallIdHeader.NAME))
.getCallId());
if (authorization != null)
{
invite.setHeader(authorization);
}
try
{
invite.setContent(sdpOffer, protocolProvider.getHeaderFactory()
.createContentTypeHeader("application", "sdp"));
}
catch (ParseException ex)
{
throwOperationFailedException(
"Failed to parse SDP offer for the new invite.",
OperationFailedException.INTERNAL_ERROR, ex);
}
ClientTransaction clientTransaction = null;
try
{
clientTransaction =
sipParticipant.getJainSipProvider().getNewClientTransaction(
invite);
}
catch (TransactionUnavailableException ex)
{
throwOperationFailedException(
"Failed to create a client transaction for the new invite.",
OperationFailedException.INTERNAL_ERROR, ex);
}
try
{
dialog.sendRequest(clientTransaction);
}
catch (SipException ex)
{
throwOperationFailedException("Failed to send the new invite.",
OperationFailedException.NETWORK_FAILURE, ex);
}
}
/**
* Logs a specific message and associated <tt>Throwable</tt> cause as an
* error using the current <tt>Logger</tt> and then throws a new
* <tt>OperationFailedException</tt> with the message, a specific error
* code and the cause.
*
* @param message the message to be logged and then wrapped in a new
* <tt>OperationFailedException</tt>
* @param errorCode the error code to be assigned to the new
* <tt>OperationFailedException</tt>
* @param cause the <tt>Throwable</tt> that has caused the necessity to
* log an error and have a new <tt>OperationFailedException</tt>
* thrown
* @throws OperationFailedException
*/
private void throwOperationFailedException(String message, int errorCode,
Throwable cause) throws OperationFailedException
{
/** @todo implement putOnHold() */
logger.error(message, cause);
throw new OperationFailedException(message, errorCode, cause);
}
/**
@ -432,16 +593,19 @@ public void processRequest(RequestEvent requestEvent)
if (request.getMethod().equals(Request.INVITE))
{
logger.debug("received INVITE");
if (serverTransaction.getDialog().getState() == null)
DialogState dialogState = serverTransaction.getDialog().getState();
if ((dialogState == null)
|| dialogState.equals(DialogState.CONFIRMED))
{
if (logger.isDebugEnabled())
logger.debug("request is an INVITE. Dialog state="
+ serverTransaction.getDialog().getState());
+ dialogState);
processInvite(jainSipProvider, serverTransaction, request);
}
else
{
logger.error("reINVITE-s are not currently supported.");
logger
.error("reINVITEs while the dialog is not confirmed are not currently supported.");
}
}
//ACK
@ -572,8 +736,11 @@ private void processTrying(ClientTransaction clientTransaction,
return;
}
//change status
callParticipant.setState(CallParticipantState.CONNECTING);
// change status
CallParticipantState callParticipantState = callParticipant.getState();
if (!CallParticipantState.CONNECTED.equals(callParticipantState)
&& !CallParticipantState.isOnHold(callParticipantState))
callParticipant.setState(CallParticipantState.CONNECTING);
}
/**
@ -677,15 +844,11 @@ private void processInviteOK(ClientTransaction clientTransaction,
return;
}
}
if (callParticipant.getState() == CallParticipantState.CONNECTED)
{
// This can happen if the OK UDP packet has been resent due to a
//timeout. (fix by Michael Koch)
logger.debug("Ignoring invite OK since call participant is "
+"already connected.");
return;
}
/*
* Receiving an Invite OK is allowed even when the participant is
* already connected for the purposes of call hold.
*/
Request ack = null;
ContentTypeHeader contentTypeHeader = null;
@ -777,9 +940,13 @@ private void processInviteOK(ClientTransaction clientTransaction,
return;
}
}
callSession.processSdpAnswer(callParticipant
, callParticipant.getSdpDescription());
CallParticipantState callParticipantState = callParticipant.getState();
if ((callParticipantState != CallParticipantState.CONNECTED)
&& !CallParticipantState.isOnHold(callParticipantState))
{
callSession.processSdpAnswer(callParticipant, callParticipant
.getSdpDescription());
}
//set the call url in case there was one
/** @todo this should be done in CallSession, once we move
* it here.*/
@ -812,8 +979,9 @@ private void processInviteOK(ClientTransaction clientTransaction,
+ exc.getMessage());
}
//change status
callParticipant.setState(CallParticipantState.CONNECTED);
// change status
if (!CallParticipantState.isOnHold(callParticipant.getState()))
callParticipant.setState(CallParticipantState.CONNECTED);
}
/**
@ -1166,12 +1334,21 @@ private void processInvite( SipProvider sourceProvider,
ServerTransaction serverTransaction,
Request invite)
{
logger.trace("Creating call participant.");
Dialog dialog = serverTransaction.getDialog();
CallParticipantSipImpl callParticipant
= createCallParticipantFor(serverTransaction, sourceProvider);
logger.trace("call participant created = " + callParticipant);
CallParticipantSipImpl callParticipant =
activeCallsRepository.findCallParticipant(dialog);
int statusCode;
if (callParticipant == null)
{
statusCode = Response.RINGING;
logger.trace("Creating call participant.");
callParticipant =
createCallParticipantFor(serverTransaction, sourceProvider);
logger.trace("call participant created = " + callParticipant);
}
else
statusCode = Response.OK;
//sdp description may be in acks - bug report Laurent Michel
ContentLengthHeader cl = invite.getContentLength();
@ -1241,21 +1418,38 @@ private void processInvite( SipProvider sourceProvider,
}
}
//Send RINGING
logger.debug("Invite seems ok, we'll say RINGING.");
Response ringing = null;
// Send statusCode
String statusCodeString =
(statusCode == Response.RINGING) ? "RINGING" : "OK";
logger.debug("Invite seems ok, we'll say " + statusCodeString + ".");
Response response = null;
try {
ringing = protocolProvider.getMessageFactory().createResponse(
Response.RINGING, invite);
protocolProvider.attachToTag(ringing, dialog);
ringing.setHeader(protocolProvider.getSipCommUserAgentHeader());
response = protocolProvider.getMessageFactory().createResponse(
statusCode, invite);
protocolProvider.attachToTag(response, dialog);
response.setHeader(protocolProvider.getSipCommUserAgentHeader());
//set our display name
((ToHeader)ringing.getHeader(ToHeader.NAME))
((ToHeader)response.getHeader(ToHeader.NAME))
.getAddress().setDisplayName(protocolProvider
.getOurDisplayName());
ringing.addHeader(protocolProvider.getContactHeader());
response.addHeader(protocolProvider.getContactHeader());
if (statusCode != Response.RINGING)
{
try
{
processInviteSendingResponse(callParticipant, response);
}
catch (OperationFailedException ex)
{
logger.error("Error while trying to send a request", ex);
callParticipant.setState(CallParticipantState.FAILED,
"Internal Error: " + ex.getMessage());
return;
}
}
}
catch (ParseException ex) {
logger.error("Error while trying to send a request"
@ -1265,9 +1459,10 @@ private void processInvite( SipProvider sourceProvider,
return;
}
try {
logger.trace("will send ringing response: ");
serverTransaction.sendResponse(ringing);
logger.debug("sent a ringing response: " + ringing);
logger.trace("will send " + statusCodeString + " response: ");
serverTransaction.sendResponse(response);
logger
.debug("sent a " + statusCodeString + " response: " + response);
}
catch (Exception ex) {
logger.error("Error while trying to send a request"
@ -1277,6 +1472,127 @@ private void processInvite( SipProvider sourceProvider,
, "Internal Error: " + ex.getMessage());
return;
}
if (statusCode != Response.RINGING)
{
try
{
processInviteSentResponse(callParticipant, response);
}
catch (OperationFailedException ex)
{
logger.error("Error after sending a request", ex);
}
}
}
/**
* Provides a hook for this instance to take last configuration steps on a
* specific <tt>Response</tt> before it is sent to a specific
* <tt>CallParticipant</tt> as part of the execution of
* {@link #processInvite(SipProvider, ServerTransaction, Request)}.
*
* @param participant the <tt>CallParticipant</tt> to receive a specific
* <tt>Response</tt>
* @param response the <tt>Response</tt> to be sent to the
* <tt>participant</tt>
* @throws OperationFailedException
* @throws ParseException
*/
private void processInviteSendingResponse(CallParticipant participant,
Response response) throws OperationFailedException, ParseException
{
/*
* At the time of this writing, we're only getting called because a
* response to a call-hold invite is to be sent.
*/
CallSession callSession =
((CallSipImpl) participant.getCall()).getMediaCallSession();
CallParticipantSipImpl sipParticipant =
(CallParticipantSipImpl) participant;
String sdpOffer = sipParticipant.getSdpDescription();
String sdpAnswer = null;
try
{
sdpAnswer =
callSession.createSdpDescriptionForHold(sdpOffer, callSession
.isSdpOfferToHold(sdpOffer));
}
catch (MediaException ex)
{
throwOperationFailedException(
"Failed to create SDP answer to put-on/off-hold request.",
OperationFailedException.INTERNAL_ERROR, ex);
}
response.setContent(sdpAnswer, protocolProvider.getHeaderFactory()
.createContentTypeHeader("application", "sdp"));
}
/**
* Provides a hook for this instance to take immediate steps after a
* specific <tt>Response</tt> has been sent to a specific
* <tt>CallParticipant</tt> as part of the execution of
* {@link #processInvite(SipProvider, ServerTransaction, Request)}.
*
* @param participant the <tt>CallParticipant</tt> who was sent a specific
* <tt>Response</tt>
* @param response the <tt>Response</tt> that has just been sent to the
* <tt>participant</tt>
* @throws OperationFailedException
* @throws ParseException
*/
private void processInviteSentResponse(CallParticipant participant,
Response response) throws OperationFailedException
{
/*
* At the time of this writing, we're only getting called because a
* response to a call-hold invite is to be sent.
*/
CallSession callSession =
((CallSipImpl) participant.getCall()).getMediaCallSession();
CallParticipantSipImpl sipParticipant =
(CallParticipantSipImpl) participant;
boolean on = false;
try
{
on =
callSession
.isSdpOfferToHold(sipParticipant.getSdpDescription());
}
catch (MediaException ex)
{
throwOperationFailedException(
"Failed to create SDP answer to put-on/off-hold request.",
OperationFailedException.INTERNAL_ERROR, ex);
}
callSession.putOnHold(on, false);
CallParticipantState state = sipParticipant.getState();
if (CallParticipantState.ON_HOLD_LOCALLY.equals(state))
{
if (on)
sipParticipant.setState(CallParticipantState.ON_HOLD_MUTUALLY);
}
else if (CallParticipantState.ON_HOLD_MUTUALLY.equals(state))
{
if (!on)
sipParticipant.setState(CallParticipantState.ON_HOLD_LOCALLY);
}
else if (CallParticipantState.ON_HOLD_REMOTELY.equals(state))
{
if (!on)
sipParticipant.setState(CallParticipantState.CONNECTED);
}
else if (on)
{
sipParticipant.setState(CallParticipantState.ON_HOLD_REMOTELY);
}
}
/**
@ -1358,8 +1674,9 @@ void processAck(ServerTransaction serverTransaction,
callParticipant.setSdpDescription(
new String(ackRequest.getRawContent()));
}
//change status
callParticipant.setState(CallParticipantState.CONNECTED);
// change status
if (!CallParticipantState.isOnHold(callParticipant.getState()))
callParticipant.setState(CallParticipantState.CONNECTED);
}
/**
@ -1470,16 +1787,16 @@ public synchronized void hangupCallParticipant(CallParticipant participant)
CallParticipantSipImpl callParticipant
= (CallParticipantSipImpl)participant;
Dialog dialog = callParticipant.getDialog();
if (callParticipant.getState().equals(CallParticipantState.CONNECTED))
CallParticipantState participantState = callParticipant.getState();
if (participantState.equals(CallParticipantState.CONNECTED)
|| CallParticipantState.isOnHold(participantState))
{
sayBye(callParticipant);
callParticipant.setState(CallParticipantState.DISCONNECTED);
}
else if (callParticipant.getState()
.equals(CallParticipantState.CONNECTING)
|| callParticipant.getState()
.equals(CallParticipantState.ALERTING_REMOTE_SIDE))
else if (participantState.equals(CallParticipantState.CONNECTING)
|| participantState
.equals(CallParticipantState.ALERTING_REMOTE_SIDE))
{
if (callParticipant.getFirstTransaction() != null)
{
@ -1489,18 +1806,17 @@ else if (callParticipant.getState()
}
callParticipant.setState(CallParticipantState.DISCONNECTED);
}
else if (callParticipant.getState()
.equals(CallParticipantState.INCOMING_CALL))
else if (participantState.equals(CallParticipantState.INCOMING_CALL))
{
callParticipant.setState(CallParticipantState.DISCONNECTED);
sayBusyHere(callParticipant);
}
//For FAILE and BUSY we only need to update CALL_STATUS
else if (callParticipant.getState().equals(CallParticipantState.BUSY))
else if (participantState.equals(CallParticipantState.BUSY))
{
callParticipant.setState(CallParticipantState.DISCONNECTED);
}
else if (callParticipant.getState().equals(CallParticipantState.FAILED))
else if (participantState.equals(CallParticipantState.FAILED))
{
callParticipant.setState(CallParticipantState.DISCONNECTED);
}
@ -1783,7 +2099,10 @@ public synchronized void answerCallParticipant(CallParticipant participant)
, OperationFailedException.INTERNAL_ERROR);
}
if(participant.getState().equals(CallParticipantState.CONNECTED))
CallParticipantState participantState = participant.getState();
if (participantState.equals(CallParticipantState.CONNECTED)
|| CallParticipantState.isOnHold(participantState))
{
logger.info("Ignoring user request to answer a CallParticipant "
+ "that is already connected. CP:" + participant);

@ -20,10 +20,11 @@
* single <tt>Call</tt> may only be associated one <tt>CallSession</tt>
* instance.
* <p>
* A call session also allows signalling protocols to generate SDP offers and
* A call session also allows signaling protocols to generate SDP offers and
* construct sdp answers.
*
* @author Emil Ivov
* @author Lubomir Marinov
*/
public interface CallSession
{
@ -31,7 +32,7 @@ public interface CallSession
* The method is meant for use by protocol service implementations when
* willing to send an invitation to a remote callee.
*
* @return a String containing an SDP offer descibing parameters of the
* @return a String containing an SDP offer describing parameters of the
* <tt>Call</tt> associated with this session.
* @throws MediaException code INTERNAL_ERROR if generating the offer fails
* for some reason.
@ -56,6 +57,54 @@ public String createSdpOffer()
public String createSdpOffer(InetAddress intendedDestination)
throws MediaException;
/**
* The method is meant for use by protocol service implementations when
* willing to send an in-dialog invitation to a remote callee to put her
* on/off hold or to send an answer to an offer to be put on/off hold.
*
* @param participantSdpDescription the last SDP description of the remote
* callee
* @param on <tt>true</tt> if the SDP description should offer the remote
* callee to be put on hold or answer an offer from the remote
* callee to be put on hold; <tt>false</tt> to work in the
* context of a put-off-hold offer
* @return an SDP description <tt>String</tt> which offers the remote
* callee to be put her on/off hold or answers an offer from the
* remote callee to be put on/off hold
* @throws MediaException
*/
public String createSdpDescriptionForHold(String participantSdpDescription,
boolean on) throws MediaException;
/**
* Determines whether a specific SDP description <tt>String</tt> offers
* this party to be put on hold.
*
* @param sdpOffer the SDP description <tt>String</tt> to be examined for
* an offer to this party to be put on hold
* @return <tt>true</tt> if the specified SDP description <tt>String</tt>
* offers this party to be put on hold; <tt>false</tt>, otherwise
* @throws MediaException
*/
public boolean isSdpOfferToHold(String sdpOffer) throws MediaException;
/**
* Puts the media of this <tt>CallSession</tt> on/off hold depending on
* the origin of the request.
* <p>
* For example, a remote request to have this party put off hold cannot
* override an earlier local request to put the remote party on hold.
* </p>
*
* @param on <tt>true</tt> to request the media of this
* <tt>CallSession</tt> be put on hold; <tt>false</tt>,
* otherwise
* @param here <tt>true</tt> if the request comes from this side of the
* call; <tt>false</tt> if the remote party is the issuer of
* the request i.e. it's the result of a remote offer
*/
public void putOnHold(boolean on, boolean here);
/**
* The method is meant for use by protocol service implementations when
* willing to respond to an invitation received from a remote caller. Apart
@ -65,7 +114,7 @@ public String createSdpOffer(InetAddress intendedDestination)
* @param sdpOffer the SDP offer that we'd like to create an answer for.
* @param offerer the participant that has sent the offer.
*
* @return a String containing an SDP answer descibing parameters of the
* @return a String containing an SDP answer describing parameters of the
* <tt>Call</tt> associated with this session and matching those advertised
* by the caller in their <tt>sdpOffer</tt>.
*
@ -111,7 +160,7 @@ public void processSdpAnswer(CallParticipant responder, String sdpAnswer)
public int getAudioPort();
/**
* Returns a URL pointing ta a location with call control information for
* Returns a URL pointing to a location with call control information for
* this call or <tt>null</tt> if no such URL is available.
*
* @return a URL link to a location with call information or a call control

@ -27,12 +27,13 @@
* <p>A FAILED state is prone to appear at any place in the above diagram and is
* generally followed by a disconnected state.
*
* <p>Information on call participant is shonw in the phone user interface until
* <p>Information on call participant is shown in the phone user interface until
* they enter the DISCONNECTED state. At that point call participant information
* is automatically removed from the user interface and the call is considered
* terminated.
*
* @author Emil Ivov
* @author Lubomir Marinov
*/
public class CallParticipantState
{
@ -179,18 +180,67 @@ public class CallParticipantState
= new CallParticipantState(_FAILED);
/**
* This constant value indicates a String representation of the ON_HOLD
* call state.
* <br>This constant has the String value "On Hold".
* The constant value being a String representation of the ON_HOLD_LOCALLY
* call participant state.
* <p>
* This constant has the String value "Locally On Hold".
* </p>
*/
public static final String _ON_HOLD = "On Hold";
public static final String _ON_HOLD_LOCALLY = "Locally On Hold";
/**
* This constant value indicates that the state of the call participant is
* is ON_HOLD - which means that an attempt to establish a call with that
* participant has failed for an unexpected reason.
* The constant value indicating that the state of a call participant is
* locally put on hold.
*/
public static final CallParticipantState ON_HOLD_LOCALLY
= new CallParticipantState(_ON_HOLD_LOCALLY);
/**
* The constant value being a String representation of the ON_HOLD_MUTUALLY
* call participant state.
* <p>
* This constant has the String value "Mutually On Hold".
* </p>
*/
public static final CallParticipantState ON_HOLD
= new CallParticipantState(_ON_HOLD);
public static final String _ON_HOLD_MUTUALLY = "Mutually On Hold";
/**
* The constant value indicating that the state of a call participant is
* mutually - locally and remotely - put on hold.
*/
public static final CallParticipantState ON_HOLD_MUTUALLY
= new CallParticipantState(_ON_HOLD_MUTUALLY);
/**
* The constant value being a String representation of the ON_HOLD_REMOTELY
* call participant state.
* <p>
* This constant has the String value "Remotely On Hold".
* </p>
*/
public static final String _ON_HOLD_REMOTELY = "Remotely On Hold";
/**
* The constant value indicating that the state of a call participant is
* remotely put on hold.
*/
public static final CallParticipantState ON_HOLD_REMOTELY
= new CallParticipantState(_ON_HOLD_REMOTELY);
/**
* Determines whether a specific <tt>CallParticipantState</tt> value
* signal a call hold regardless of the issuer (which may be local and/or
* remote).
*
* @param state
* the <tt>CallParticipantState</tt> value to be checked
* whether it signals a call hold
* @return <tt>true</tt> if the specified <tt>state</tt> signals a call
* hold; <tt>false</tt>, otherwise
*/
public static final boolean isOnHold(CallParticipantState state)
{
return CallParticipantState.ON_HOLD_LOCALLY.equals(state)
|| CallParticipantState.ON_HOLD_MUTUALLY.equals(state)
|| CallParticipantState.ON_HOLD_REMOTELY.equals(state);
}
/**
* A string representationf this Participant Call State. Could be
@ -221,10 +271,10 @@ public String getStateString()
}
/**
* Returns a string represenation of this call state. Strings returned
* Returns a string representation of this call state. Strings returned
* by this method have the following format:
* CallParticipantState:<STATE_STRING>
* and are meant to be used for loggin/debugging purposes.
* and are meant to be used for logging/debugging purposes.
* @return a string representation of this object.
*/
public String toString()

@ -96,9 +96,13 @@ public void putOnHold(CallParticipant participant)
* Resumes communication with a call participant previously put on hold. If
* the specified participant is not "On Hold" at the time putOffHold is
* called, the method has no effect.
*
* @param participant the call participant to put on hold.
* @throws OperationFailedException with the corresponding code if we
* encounter an error while performing this operation
*/
public void putOffHold(CallParticipant participant);
public void putOffHold(CallParticipant participant)
throws OperationFailedException;
/**
* Indicates a user request to end a call with the specified call

@ -0,0 +1,86 @@
/*
* 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.event;
/**
* An abstract adapter class for receiving call participant (change) events.
* This class exists only as a convenience for creating listener objects.
* <p>
* Extend this class to create a <tt>CallParticipantChangeEvent</tt> listener
* and override the methods for the events of interest. (If you implement the
* <tt>CallParticipantListener</tt> interface, you have to define all of the
* methods in it. This abstract class defines null methods for them all, so you
* only have to define methods for events you care about.)
* </p>
*
* @see CallParticipantChangeEvent
* @see CallParticipantListener
*
* @author Lubomir Marinov
*/
public abstract class CallParticipantAdapter
implements CallParticipantListener
{
/**
* Indicates that a change has occurred in the address of the source
* CallParticipant.
*
* @param evt The <tt>CallParticipantChangeEvent</tt> instance containing
* the source event as well as its previous and its new address.
*/
public void participantAddressChanged(CallParticipantChangeEvent evt)
{
}
/**
* Indicates that a change has occurred in the display name of the source
* CallParticipant.
*
* @param evt The <tt>CallParticipantChangeEvent</tt> instance containing
* the source event as well as its previous and its new display
* names.
*/
public void participantDisplayNameChanged(CallParticipantChangeEvent evt)
{
}
/**
* Indicates that a change has occurred in the image of the source
* CallParticipant.
*
* @param evt The <tt>CallParticipantChangeEvent</tt> instance containing
* the source event as well as its previous and its new image.
*/
public void participantImageChanged(CallParticipantChangeEvent evt)
{
}
/**
* Indicates that a change has occurred in the status of the source
* CallParticipant.
*
* @param evt The <tt>CallParticipantChangeEvent</tt> instance containing
* the source event as well as its previous and its new status.
*/
public void participantStateChanged(CallParticipantChangeEvent evt)
{
}
/**
* Indicates that a change has occurred in the transport address that we use
* to communicate with the participant.
*
* @param evt The <tt>CallParticipantChangeEvent</tt> instance containing
* the source event as well as its previous and its new transport
* address.
*/
public void participantTransportAddressChanged(
CallParticipantChangeEvent evt)
{
}
}

@ -858,7 +858,7 @@ public void callEnded(CallEvent event)
* status changes.
*/
public class CallParticipantStateEventCollector
implements CallParticipantListener
extends CallParticipantAdapter
{
public ArrayList collectedEvents = new ArrayList();
private CallParticipant listenedCallParticipant = null;
@ -902,37 +902,6 @@ public void participantStateChanged(CallParticipantChangeEvent event)
}
}
/**
* Unused by this collector.
* @param event ignored.
*/
public void participantImageChanged(CallParticipantChangeEvent event)
{}
/**
* Unused by this collector
* @param event ignored.
*/
public void participantAddressChanged(CallParticipantChangeEvent event)
{}
/**
* Unused by this collector
* @param event ignored.
*/
public void participantTransportAddressChanged(
CallParticipantChangeEvent event)
{}
/**
* Unused by this collector
* @param event ignored.
*/
public void participantDisplayNameChanged(
CallParticipantChangeEvent event)
{}
/**
* Blocks until an event notifying us of the awaited state change is
* received or until waitFor miliseconds pass (whichever happens first).

@ -857,7 +857,7 @@ public void callEnded(CallEvent event)
* status changes.
*/
public class CallParticipantStateEventCollector
implements CallParticipantListener
extends CallParticipantAdapter
{
public ArrayList collectedEvents = new ArrayList();
private CallParticipant listenedCallParticipant = null;
@ -901,37 +901,6 @@ public void participantStateChanged(CallParticipantChangeEvent event)
}
}
/**
* Unused by this collector.
* @param event ignored.
*/
public void participantImageChanged(CallParticipantChangeEvent event)
{}
/**
* Unused by this collector
* @param event ignored.
*/
public void participantAddressChanged(CallParticipantChangeEvent event)
{}
/**
* Unused by this collector
* @param event ignored.
*/
public void participantTransportAddressChanged(
CallParticipantChangeEvent event)
{}
/**
* Unused by this collector
* @param event ignored.
*/
public void participantDisplayNameChanged(
CallParticipantChangeEvent event)
{}
/**
* Blocks until an event notifying us of the awaited state change is
* received or until waitFor miliseconds pass (whichever happens first).

Loading…
Cancel
Save