/* * 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.net.*; import java.util.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; /** * Provides a default implementation for most of the * <code>CallParticipant</code> methods with the purpose of only leaving custom * protocol development to clients using the PhoneUI service. * * @author Emil Ivov */ public abstract class AbstractCallParticipant implements CallParticipant { private static final Logger logger = Logger.getLogger(AbstractCallParticipant.class); /** * All the CallParticipant listeners registered with this CallParticipant. */ protected final List<CallParticipantListener> callParticipantListeners = new ArrayList<CallParticipantListener>(); /** * All the CallParticipantSecurityListener-s registered with this * CallParticipant. */ protected final List<CallParticipantSecurityListener> callParticipantSecurityListeners = new ArrayList<CallParticipantSecurityListener>(); /** * All the PropertyChangeListener-s registered with this CallParticipant. */ protected final List<PropertyChangeListener> propertyChangeListeners = new ArrayList<PropertyChangeListener>(); /** * The state of the call participant. */ private CallParticipantState state = CallParticipantState.UNKNOWN; private long callDurationStartTime = CALL_DURATION_START_TIME_UNKNOWN; private boolean isMute; /** * Registers the <tt>listener</tt> to the list of listeners that would be * receiving CallParticipantEvents * @param listener a listener instance to register with this participant. */ public void addCallParticipantListener(CallParticipantListener listener) { if (listener == null) return; synchronized(callParticipantListeners) { if (!callParticipantListeners.contains(listener)) callParticipantListeners.add(listener); } } /** * Unregisters the specified listener. * @param listener the listener to unregister. */ public void removeCallParticipantListener(CallParticipantListener listener) { if (listener == null) return; synchronized(callParticipantListeners) { callParticipantListeners.remove(listener); } } /** * Registers the <tt>listener</tt> to the list of listeners that would be * receiving CallParticipantSecurityEvents * * @param listener a listener instance to register with this participant. */ public void addCallParticipantSecurityListener( CallParticipantSecurityListener listener) { if (listener == null) return; synchronized(callParticipantSecurityListeners) { if (!callParticipantSecurityListeners.contains(listener)) callParticipantSecurityListeners.add(listener); } } /** * Unregisters the specified listener. * * @param listener the listener to unregister. */ public void removeCallParticipantSecurityListener( CallParticipantSecurityListener listener) { if (listener == null) return; synchronized(callParticipantSecurityListeners) { callParticipantSecurityListeners.remove(listener); } } /** * Allows the user interface to register a listener interested in property * changes. * @param listener a property change listener instance to register with this * participant. */ public void addPropertyChangeListener(PropertyChangeListener listener) { if (listener == null) return; synchronized(propertyChangeListeners) { if (!propertyChangeListeners.contains(listener)) propertyChangeListeners.add(listener); } } /** * Unregisters the specified property change listener. * * @param listener the property change listener to unregister. */ public void removePropertyChangeListener(PropertyChangeListener listener) { if (listener == null) return; synchronized(propertyChangeListeners) { propertyChangeListeners.remove(listener); } } /** * Constructs a <tt>CallParticipantChangeEvent</tt> using this call * participant as source, setting it to be of type <tt>eventType</tt> and * the corresponding <tt>oldValue</tt> and <tt>newValue</tt>, * * @param eventType the type of the event to create and dispatch. * @param oldValue the value of the source property before it changed. * @param newValue the current value of the source property. */ protected void fireCallParticipantChangeEvent(String eventType, Object oldValue, Object newValue) { this.fireCallParticipantChangeEvent( eventType, oldValue, newValue, null); } /** * Constructs a <tt>CallParticipantChangeEvent</tt> using this call * participant as source, setting it to be of type <tt>eventType</tt> and * the corresponding <tt>oldValue</tt> and <tt>newValue</tt>, * * @param eventType the type of the event to create and dispatch. * @param oldValue the value of the source property before it changed. * @param newValue the current value of the source property. * @param reason a string that could be set to contain a human readable * explanation for the transition (particularly handy when moving into a * FAILED state). */ protected void fireCallParticipantChangeEvent(String eventType, Object oldValue, Object newValue, String reason) { CallParticipantChangeEvent evt = new CallParticipantChangeEvent( this, eventType, oldValue, newValue, reason); logger.debug("Dispatching a CallParticipantChangeEvent event to " + callParticipantListeners.size() +" listeners. event is: " + evt.toString()); Iterator<CallParticipantListener> listeners = null; synchronized (callParticipantListeners) { listeners = new ArrayList<CallParticipantListener>( callParticipantListeners).iterator(); } while (listeners.hasNext()) { CallParticipantListener listener = (CallParticipantListener) listeners.next(); if(eventType.equals(CallParticipantChangeEvent .CALL_PARTICIPANT_ADDRESS_CHANGE)) { listener.participantAddressChanged(evt); } else if(eventType.equals(CallParticipantChangeEvent .CALL_PARTICIPANT_DISPLAY_NAME_CHANGE)) { listener.participantDisplayNameChanged(evt); } else if(eventType.equals(CallParticipantChangeEvent .CALL_PARTICIPANT_IMAGE_CHANGE)) { listener.participantImageChanged(evt); } else if(eventType.equals(CallParticipantChangeEvent .CALL_PARTICIPANT_STATE_CHANGE)) { listener.participantStateChanged(evt); } } } /** * Constructs a <tt>CallParticipantSecurityStatusEvent</tt> using this call * participant as source, setting it to be of type <tt>eventType</tt> and * the corresponding <tt>oldValue</tt> and <tt>newValue</tt>, * * @param sessionType the type of the session - audio or video * @param eventID the identifier of the event */ protected void fireCallParticipantSecurityOnEvent( int sessionType, String cipher, String securityString, boolean isVerified) { CallParticipantSecurityOnEvent evt = new CallParticipantSecurityOnEvent( this, sessionType, cipher, securityString, isVerified); logger.debug("Dispatching a CallParticipantSecurityStatusEvent event to " + callParticipantSecurityListeners.size() +" listeners. event is: " + evt.toString()); Iterator<CallParticipantSecurityListener> listeners = null; synchronized (callParticipantSecurityListeners) { listeners = new ArrayList<CallParticipantSecurityListener>( callParticipantSecurityListeners).iterator(); } while (listeners.hasNext()) { CallParticipantSecurityListener listener = (CallParticipantSecurityListener) listeners.next(); listener.securityOn(evt); } } /** * Constructs a <tt>CallParticipantSecurityStatusEvent</tt> using this call * participant as source, setting it to be of type <tt>eventType</tt> and * the corresponding <tt>oldValue</tt> and <tt>newValue</tt>, * * @param sessionType the type of the session - audio or video * @param eventID the identifier of the event */ protected void fireCallParticipantSecurityOffEvent(int sessionType) { CallParticipantSecurityOffEvent event = new CallParticipantSecurityOffEvent( this, sessionType); logger.debug( "Dispatching a CallParticipantSecurityAuthenticationEvent event to " + callParticipantSecurityListeners.size() +" listeners. event is: " + event.toString()); Iterator<CallParticipantSecurityListener> listeners = null; synchronized (callParticipantSecurityListeners) { listeners = new ArrayList<CallParticipantSecurityListener>( callParticipantSecurityListeners).iterator(); } while (listeners.hasNext()) { CallParticipantSecurityListener listener = (CallParticipantSecurityListener) listeners.next(); listener.securityOff(event); } } /** * Constructs a <tt>CallParticipantSecurityStatusEvent</tt> using this call * participant as source, setting it to be of type <tt>eventType</tt> and * the corresponding <tt>oldValue</tt> and <tt>newValue</tt>, * * @param sessionType the type of the session - audio or video * @param eventID the identifier of the event */ protected void fireCallParticipantSecurityMessageEvent( String messageType, String message, String i18nMessage) { CallParticipantSecurityMessageEvent evt = new CallParticipantSecurityMessageEvent( this, messageType, message, i18nMessage); logger.debug("Dispatching a CallParticipantSecurityFailedEvent event to " + callParticipantSecurityListeners.size() +" listeners. event is: " + evt.toString()); Iterator<CallParticipantSecurityListener> listeners = null; synchronized (callParticipantSecurityListeners) { listeners = new ArrayList<CallParticipantSecurityListener>( callParticipantSecurityListeners).iterator(); } while (listeners.hasNext()) { CallParticipantSecurityListener listener = (CallParticipantSecurityListener) listeners.next(); listener.securityMessageRecieved(evt); } } /** * Constructs a <tt>PropertyChangeEvent</tt> using this call * participant as source, setting the corresponding <tt>oldValue</tt> and * <tt>newValue</tt>. * * @param eventType the type of the event to create and dispatch. * @param oldValue the value of the source property before it changed. * @param newValue the current value of the source property. */ protected void firePropertyChangeEvent( String propertyName, Object oldValue, Object newValue) { PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName, oldValue, newValue); logger.debug("Dispatching a PropertyChangeEvent event to " + propertyChangeListeners.size() + " listeners. event is: " + event.toString()); Iterator<PropertyChangeListener> listeners = null; synchronized (propertyChangeListeners) { listeners = new ArrayList<PropertyChangeListener>( propertyChangeListeners).iterator(); } while (listeners.hasNext()) { PropertyChangeListener listener = (PropertyChangeListener) listeners.next(); listener.propertyChange(event); } } /** * Returns a string representation of the participant in the form of * <br/> * Display Name <address>;status=CallParticipantStatus * @return a string representation of the participant and its state. */ public String toString() { return getDisplayName() + " <" + getAddress() + ">;status=" + getState().getStateString(); } /** * Returns a URL pointing ta a location with call control information for * this participant or <tt>null</tt> if no such URL is available for this * call participant. * * @return a URL link to a location with call information or a call control * web interface related to this participant or <tt>null</tt> if no such URL * is available. */ public URL getCallInfoURL() { //if signaling protocols (such as SIP) know where to get this URL from //they should override this method return null; } /** * Returns an object representing the current state of that participant. * * @return a CallParticipantState instance representin the participant's * state. */ public CallParticipantState getState() { return state; } /** * Causes this CallParticipant to enter the specified state. The method also * sets the currentStateStartDate field and fires a * CallParticipantChangeEvent. * * @param newState the state this call participant should enter. * @param reason a string that could be set to contain a human readable * explanation for the transition (particularly handy when moving into a * FAILED state). */ public void setState(CallParticipantState newState, String reason) { CallParticipantState oldState = getState(); if(oldState == newState) return; this.state = newState; if (CallParticipantState.CONNECTED.equals(newState) && !CallParticipantState.isOnHold(oldState)) { callDurationStartTime = System.currentTimeMillis(); } fireCallParticipantChangeEvent( CallParticipantChangeEvent.CALL_PARTICIPANT_STATE_CHANGE, oldState, newState); } /** * Causes this CallParticipant to enter the specified state. The method also * sets the currentStateStartDate field and fires a * CallParticipantChangeEvent. * * @param newState the state this call participant should enter. */ public void setState(CallParticipantState newState) { setState(newState, null); } /** * Gets the time at which this <code>CallParticipant</code> transitioned * into a state (likely {@link CallParticipantState#CONNECTED}) marking the * start of the duration of the participation in a <code>Call</code>. * * @return the time at which this <code>CallParticipant</code> transitioned * into a state marking the start of the duration of the * participation in a <code>Call</code> or * {@link CallParticipant#CALL_DURATION_START_TIME_UNKNOWN} if such * a transition has not been performed */ public long getCallDurationStartTime() { return callDurationStartTime; } /** * Determines whether the audio stream (if any) being sent to this * participant is mute. * <p> * The default implementation returns <tt>false</tt>. * </p> * * @return <tt>true</tt> if an audio stream is being sent to this * participant and it is currently mute; <tt>false</tt>, otherwise */ public boolean isMute() { return false; } /** * Sets the mute property for this call participant. * * @param mute the new value of the mute property for this call participant */ public void setMute(boolean newMuteValue) { firePropertyChangeEvent(MUTE_PROPERTY_NAME, isMute, newMuteValue); this.isMute = newMuteValue; } /** * Sets the security status for this call participant. * * @param isSecurityOn <code>true</code> to indicate that the security is * turned on and <code>false</code> - otherwise. * @param sessionType the type of the call session - audio or video. */ public void setSecurityOn( boolean isSecurityOn, int sessionType, String cipher, String securityString, boolean isVerified) { if (isSecurityOn) fireCallParticipantSecurityOnEvent( sessionType, cipher, securityString, isVerified); } public void setSecurityOff(int sessionType) { fireCallParticipantSecurityOffEvent(sessionType); } /** * Sets the security message associated with a failure/warning or * information coming from the encryption protocol. * * @param messageType the type of the message. * @param message the message */ public void setSecurityMessage( String messageType, String message, String i18nMessage) { fireCallParticipantSecurityMessageEvent(messageType, message, i18nMessage); } }