From bdb5b62fb155f9b65a7dbaa9060c79c78e519479 Mon Sep 17 00:00:00 2001 From: Damian Minkov Date: Wed, 1 Apr 2015 14:13:05 +0300 Subject: [PATCH] Adds OperationSetTelephonyBLF (Busy Lamp Field), can monitor lines and pickup calls if possible and sip implementation of the operation set using rfc4235. --- .../OperationSetBasicTelephonySipImpl.java | 2 +- .../OperationSetMessageWaitingSipImpl.java | 2 +- .../sip/OperationSetTelephonyBLFSipImpl.java | 710 ++++++++++++++++++ .../OperationSetVideoTelephonySipImpl.java | 2 +- .../sip/ProtocolProviderServiceSipImpl.java | 9 + .../protocol/OperationSetTelephonyBLF.java | 147 ++++ .../protocol/event/BLFStatusEvent.java | 91 +++ .../protocol/event/BLFStatusListener.java | 25 + 8 files changed, 985 insertions(+), 3 deletions(-) create mode 100644 src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyBLFSipImpl.java create mode 100644 src/net/java/sip/communicator/service/protocol/OperationSetTelephonyBLF.java create mode 100644 src/net/java/sip/communicator/service/protocol/event/BLFStatusEvent.java create mode 100644 src/net/java/sip/communicator/service/protocol/event/BLFStatusListener.java 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 bce6628b3..0d2cc7677 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java @@ -173,7 +173,7 @@ protected synchronized CallSipImpl createOutgoingCall() * @throws OperationFailedException with the corresponding code if we fail * to create the call. */ - private synchronized CallSipImpl createOutgoingCall( + synchronized CallSipImpl createOutgoingCall( Address calleeAddress, javax.sip.message.Message cause, CallConference conference) diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetMessageWaitingSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetMessageWaitingSipImpl.java index 3b149d48c..702e6efea 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetMessageWaitingSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetMessageWaitingSipImpl.java @@ -179,7 +179,7 @@ public void removeMessageWaitingNotificationListener( { List l = this.messageWaitingNotificationListeners.get(type); if(l != null) - this.messageWaitingNotificationListeners.remove(listener); + l.remove(listener); } } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyBLFSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyBLFSipImpl.java new file mode 100644 index 000000000..f226df185 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyBLFSipImpl.java @@ -0,0 +1,710 @@ +/* + * Jitsi, 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.protocol.sip; + +import gov.nist.javax.sip.header.*; +import gov.nist.javax.sip.header.extensions.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.Logger; +import org.jitsi.util.*; +import org.jitsi.util.xml.*; +import org.w3c.dom.*; + +import javax.sip.*; +import javax.sip.address.*; +import javax.sip.message.*; +import java.net.*; +import java.text.*; +import java.util.*; + +/** + * Provides operations necessary to monitor line activity and pickup calls + * if needed. BLF stands for Busy Lamp Field. + * Implementation using rfc4235 and rfc5359#section-2.16. + * + * @author Damian Minkov + */ +public class OperationSetTelephonyBLFSipImpl + implements OperationSetTelephonyBLF, + RegistrationStateChangeListener +{ + /** + * Our class logger. + */ + private static final Logger logger + = Logger.getLogger(OperationSetTelephonyBLFSipImpl.class); + + /** + * Account property to enable/disable OperationSetTelephonyBLF. + */ + public static final String BLF_ENABLED_ACC_PROP = "BLF_ENABLED"; + + /** + * Account property prefix to set/provision monitored lines. + */ + public static final String BLF_LINE_ACC_PROP_PREFIX = "BLF_LINE"; + + /** + * Account property suffix to set/provision monitored line address. + */ + public static final String BLF_LINE_ADDR_ACC_PROP_SUFFIX = "Address"; + /** + * Account property suffix to set/provision monitored line display name. + */ + public static final String BLF_LINE_NAME_ACC_PROP_PREFIX = "Name"; + /** + * Account property suffix to set/provision monitored line group. + */ + public static final String BLF_LINE_GROUP_ACC_PROP_PREFIX = "Group"; + + /** + * The name of the event package supported by + * BLFServiceImpl in SUBSCRIBE + * and NOTIFY requests. + */ + static final String EVENT_PACKAGE = "dialog"; + + /** + * The content sub-type of the content supported in NOTIFY requests handled + * by OperationSetTelephonyBLFSipImpl. + */ + private static final String CONTENT_SUB_TYPE = "dialog-info+xml"; + + /** + * The time in seconds after which a Subscription should be expired + * by the OperationSetTelephonyBLFSipImpl instance + * which manages it. + */ + private static final int SUBSCRIPTION_DURATION = 3600; + + /** + * How many seconds before a timeout should we refresh our state + */ + private static final int REFRESH_MARGIN = 60; + + /** + * A list of listeners. + */ + private final List listeners + = new ArrayList(); + + /** + * List of monitored lines. + */ + private List lines = new ArrayList(); + + /** + * The parent provider. + */ + private ProtocolProviderServiceSipImpl provider; + + /** + * The EventPackageSubscriber which provides the ability of + * this instance to act as a subscriber for the dialog+info event package. + */ + private final EventPackageSubscriber subscriber; + + /** + * The timer which will handle all the scheduled tasks + */ + private final TimerScheduler timer = new TimerScheduler(); + + /** + * Namespace wildcard. + */ + private static final String ANY_NS = "*"; + + /** + * The property to access details for the dataObject Line. + */ + private static final String DATA_PROP + = LineDetails.class.getCanonicalName(); + + /** + * Constructs the operations set and initializes the monitored lines. + * @param provider + */ + public OperationSetTelephonyBLFSipImpl( + ProtocolProviderServiceSipImpl provider) + { + this.provider = provider; + this.provider.addRegistrationStateChangeListener(this); + + initLines(); + + this.subscriber + = new EventPackageSubscriber( + this.provider, + EVENT_PACKAGE, + SUBSCRIPTION_DURATION, + CONTENT_SUB_TYPE, + this.timer, + REFRESH_MARGIN); + this.provider.registerEvent(EVENT_PACKAGE); + } + + /** + * Initializes the lines from the account properties. + */ + private void initLines() + { + Map props + = provider.getAccountID().getAccountProperties(); + + Map lines + = new HashMap(); + + // parse the properties into the map where the index is the key + for(Map.Entry entry : props.entrySet()) + { + String pName = entry.getKey(); + String entryValue = entry.getValue(); + String ix; + + if(!pName.startsWith(BLF_LINE_ACC_PROP_PREFIX) || entryValue == null) + continue; + + entryValue = entryValue.trim(); + + if(!pName.contains(".")) + continue; + + pName = pName.replaceAll(BLF_LINE_ACC_PROP_PREFIX + ".", ""); + + if(!pName.contains(".")) + continue; + + ix = pName.substring(0, pName.lastIndexOf('.')).trim(); + + String[] lineValues = lines.get(ix); + if(lineValues == null) + { + lineValues = new String[3]; + lines.put(ix, lineValues); + } + + if(pName.contains(BLF_LINE_ADDR_ACC_PROP_SUFFIX)) + { + lineValues[0] = entryValue; + } + else if(pName.contains(BLF_LINE_NAME_ACC_PROP_PREFIX)) + { + lineValues[1] = entryValue; + } + else if(pName.contains(BLF_LINE_GROUP_ACC_PROP_PREFIX)) + { + lineValues[2] = entryValue; + } + } + + for(Map.Entry en : lines.entrySet()) + { + String[] vals = en.getValue(); + + this.lines.add(new Line(vals[0], vals[1], vals[2], this.provider)); + } + } + + /** + * Adds BLFStatus listener + * @param listener the listener to add. + */ + @Override + public void addStatusListener(BLFStatusListener listener) + { + synchronized (this.listeners) + { + if (!this.listeners.contains(listener)) + { + this.listeners.add(listener); + } + } + } + + /** + * Removes BLFStatus listener. + * @param listener the listener to remove. + */ + @Override + public void removeStatusListener(BLFStatusListener listener) + { + synchronized (this.listeners) + { + this.listeners.remove(listener); + } + } + + /** + * To pickup the call for the monitored line if possible. + * + * @param line to try to pick up. + */ + @Override + public void pickup(Line line) + throws OperationFailedException + { + LineDetails details = (LineDetails)line.getData(DATA_PROP); + if(details == null) + return; + + if(StringUtils.isNullOrEmpty(details.callID) + || StringUtils.isNullOrEmpty(details.localTag) + || StringUtils.isNullOrEmpty(details.remoteTag)) + return; + + // replaces + Address targetAddress = null; + try + { + targetAddress = provider.parseAddressString(line.getAddress()); + } + catch (ParseException ex) + { + ProtocolProviderServiceSipImpl.throwOperationFailedException( + "Failed to parse address string " + line.getAddress(), + OperationFailedException.ILLEGAL_ARGUMENT, ex, logger); + } + + Replaces replacesHeader = null; + SipURI sipURI = (SipURI) targetAddress.getURI(); + + try + { + replacesHeader = (Replaces) + ((HeaderFactoryImpl) provider.getHeaderFactory()) + .createReplacesHeader( + details.callID, + details.remoteTag, + details.localTag); + } + catch (ParseException ex) + { + ProtocolProviderServiceSipImpl.throwOperationFailedException( + "Failed to create Replaces header for target call-id " + + details.callID, + OperationFailedException.ILLEGAL_ARGUMENT, ex, logger); + } + try + { + sipURI.setHeader(ReplacesHeader.NAME, + URLEncoder.encode(replacesHeader.encodeBody( + new StringBuilder()).toString(), "UTF-8")); + } + catch (Exception ex) + { + //ParseException or UnsupportedEncodingException + ProtocolProviderServiceSipImpl.throwOperationFailedException( + "Failed to set Replaces header " + replacesHeader + + " to SipURI " + sipURI, + OperationFailedException.INTERNAL_ERROR, ex, logger); + } + + OperationSetBasicTelephonySipImpl telOpSet + = (OperationSetBasicTelephonySipImpl)provider + .getOperationSet(OperationSetBasicTelephony.class); + + CallSipImpl call + = telOpSet.createOutgoingCall(targetAddress, null, null); + } + + /** + * List of currently monitored lines. + * @return list of currently monitored lines. + */ + @Override + public List getCurrentlyMonitoredLines() + { + return new ArrayList(this.lines); + } + + @Override + public void registrationStateChanged(RegistrationStateChangeEvent evt) + { + if(evt.getNewState().equals(RegistrationState.REGISTERED)) + { + for(Line line : this.lines) + { + // Attempt to subscribe. + try + { + subscriber.poll(new DialogInfoSubscriberSubscription( + provider.parseAddressString(line.getAddress()), line)); + } + catch (OperationFailedException ex) + { + logger.error( + "Failed to create and send the subscription", ex); + } + catch (ParseException ex) + { + logger.error( + "Failed to create and send the subscription", ex); + } + } + } + else if(evt.getNewState().equals(RegistrationState.UNREGISTERING)) + { + timer.cancel(); + } + else if(evt.getNewState().equals( + RegistrationState.CONNECTION_FAILED) + || evt.getNewState().equals( + RegistrationState.AUTHENTICATION_FAILED) + || evt.getNewState().equals( + RegistrationState.UNREGISTERED)) + { + if (subscriber != null) + { + for(Line line : this.lines) + { + try + { + subscriber.removeSubscription( + provider.parseAddressString(line.getAddress())); + } + catch(ParseException ex) + { + if(logger.isDebugEnabled()) + { + logger.debug("Failed to remove subscription for " + + line.getAddress()); + } + } + + fireEvent(line, BLFStatusEvent.STATUS_OFFLINE); + } + } + timer.cancel(); + } + } + + /** + * Fires event. + * @param line + * @param eventType + */ + private void fireEvent(Line line, int eventType) + { + LineDetails details = (LineDetails)line.getData(DATA_PROP); + if(details == null) + { + details = new LineDetails(); + line.setData(DATA_PROP, details); + } + details.lastStatusEvent = eventType; + + BLFStatusEvent evt + = new BLFStatusEvent(line, eventType); + + Collection listeners; + synchronized (this.listeners) + { + listeners + = new ArrayList(this.listeners); + } + + if (logger.isInfoEnabled()) + logger.info( + "Dispatching BLF change. Listeners=" + + listeners.size() + + " evt=" + evt + + " line=" + line.getAddress()); + + for (BLFStatusListener listener : listeners) + listener.blfStatusChanged(evt); + } + + /** + * Convert a xml document + * + * @param document the document as a String + * + * @return a Document representing the document or null if an + * error occur + */ + Document convertDocument(byte[] document) + { + try + { + return XMLUtils.createDocument(new String(document, "UTF-8")); + } + catch (Exception e) + { + logger.error("Can't convert the string into a xml document", e); + return null; + } + } + + /** + * Secured call to XMLUtils.getText (no null returned but an empty string) + * + * @param node the node with which call XMLUtils.getText() + * + * @return the string contained in the node or an empty string if there is + * no text information in the node. + */ + private String getTextContent(Element node) + { + String res = XMLUtils.getText(node); + + if (res == null) + { + logger.warn("no text for element '" + node.getNodeName() + "'"); + return ""; + } + + return res; + } + + /** + * Represents a subscription to the dialog+info event package of a specific + * Address. + * + * @author Damian Minkov + */ + private class DialogInfoSubscriberSubscription + extends EventPackageSubscriber.Subscription + { + /** + * The line. + */ + private final Line line; + + /** + * Initializes a new PresenceSubscriberSubscription + * instance which is to represent a subscription to the dialog event + * package of a specific Address. + * + * @param address the Address which is the notifier + * the new subscription is to subscribed to + * @throws OperationFailedException if we fail extracting + * address's address. + */ + public DialogInfoSubscriberSubscription(Address address, Line line) + throws + OperationFailedException + { + super(address); + + this.line = line; + } + + /* + * Implements + * EventPackageSubscriber.Subscription#processActiveRequest(RequestEvent + * , byte[]). + */ + @Override + protected void processActiveRequest( + RequestEvent requestEvent, + byte[] rawContent) + { + if(rawContent == null) + return; + + Document doc = convertDocument(rawContent); + + if (doc == null) + return; + + if (logger.isTraceEnabled()) + logger.trace("parsing:\n" + new String(rawContent)); + + LineDetails details = (LineDetails)line.getData(DATA_PROP); + if(details == null) + { + details = new LineDetails(); + line.setData(DATA_PROP, details); + } + + // + NodeList dialogList = doc.getElementsByTagNameNS(ANY_NS, + "dialog"); + + if(dialogList.getLength() == 0) + { + // no dialogs - it is free + updateLineState(details, "Terminated"); + return; + } + + for (int i = 0; i < dialogList.getLength(); i++) + { + Node dialogNode = dialogList.item(i); + Element dialogElem = (Element)dialogNode; + + details.callID = dialogElem.getAttribute("call-id"); + details.localTag = dialogElem.getAttribute("local-tag"); + details.remoteTag = dialogElem.getAttribute("remote-tag"); + + NodeList states = ((Element)dialogNode) + .getElementsByTagNameNS(ANY_NS, "state"); + + if(states.getLength() == 0) + continue; + + updateLineState( + details, + getTextContent((Element)states.item(0))); + } + } + + /** + * Dispatch the state from the xml and set the corresponding state + * of the line. + * + * @param state is one of: Trying, Proceeding, Early, + * Confirmed, Terminated + */ + private void updateLineState(LineDetails details, String state) + { + int newEvent = BLFStatusEvent.STATUS_OFFLINE; + + switch(details.lastStatusEvent) + { + case BLFStatusEvent.STATUS_OFFLINE: + if(state.equalsIgnoreCase("Trying") + || state.equalsIgnoreCase("Proceeding") + || state.equalsIgnoreCase("Early")) + { + newEvent = BLFStatusEvent.STATUS_RINGING; + } + else if(state.equalsIgnoreCase("Confirmed")) + { + newEvent = BLFStatusEvent.STATUS_BUSY; + } + else if(state.equalsIgnoreCase("Terminated")) + { + newEvent = BLFStatusEvent.STATUS_FREE; + } + break; + case BLFStatusEvent.STATUS_FREE: + if(state.equalsIgnoreCase("Trying") + || state.equalsIgnoreCase("Proceeding") + || state.equalsIgnoreCase("Early")) + { + newEvent = BLFStatusEvent.STATUS_RINGING; + } + else if(state.equalsIgnoreCase("Confirmed")) + { + newEvent = BLFStatusEvent.STATUS_BUSY; + } + else if(state.equalsIgnoreCase("Terminated")) + { + // status is free so return + return; + } + break; + case BLFStatusEvent.STATUS_BUSY: + if(state.equalsIgnoreCase("Terminated")) + { + newEvent = BLFStatusEvent.STATUS_FREE; + } + else + { + // status is busy so return + return; + } + break; + case BLFStatusEvent.STATUS_RINGING: + if(state.equalsIgnoreCase("Confirmed")) + { + newEvent = BLFStatusEvent.STATUS_BUSY; + } + else if(state.equalsIgnoreCase("Terminated")) + { + newEvent = BLFStatusEvent.STATUS_FREE; + } + else + { + // status is ringing so return + return; + } + break; + default: + return; + } + + fireEvent(line, newEvent); + } + + /* + * Implements + * EventPackageSubscriber.Subscription#processFailureResponse( + * ResponseEvent, int). + */ + @Override + protected void processFailureResponse( + ResponseEvent responseEvent, + int statusCode) + { + fireEvent(line, BLFStatusEvent.STATUS_OFFLINE); + } + + /* + * Implements + * EventPackageSubscriber.Subscription#processSuccessResponse( + * ResponseEvent, int). + */ + @Override + protected void processSuccessResponse( + ResponseEvent responseEvent, + int statusCode) + { + switch(statusCode) + { + case Response.OK: + case Response.ACCEPTED: + fireEvent(line, BLFStatusEvent.STATUS_FREE); + break; + } + } + + /** + * Implements the corresponding SipListener method by + * terminating the corresponding subscription and polling the related + * address. + * + * @param requestEvent the event containing the request that was + * terminated. + * @param reasonCode a String indicating the reason of the termination. + */ + @Override + protected void processTerminatedRequest( + RequestEvent requestEvent, String reasonCode) + { + fireEvent(line, BLFStatusEvent.STATUS_OFFLINE); + } + } + + /** + * Details for a line. + */ + private class LineDetails + { + /** + * The current status of the line, the last event fired for it. + */ + int lastStatusEvent = BLFStatusEvent.STATUS_OFFLINE; + + /** + * call-id of the dialog if any, used for remote pickup. + */ + String callID = null; + + /** + * local-tag of the dialog if any, used for remote pickup. + */ + String localTag = null; + + /** + * remote-tag of the dialog if any, used for remote pickup. + */ + String remoteTag = null; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetVideoTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetVideoTelephonySipImpl.java index 154e2846c..33f1b8364 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetVideoTelephonySipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetVideoTelephonySipImpl.java @@ -339,7 +339,7 @@ public boolean processRequest(RequestEvent requestEvent) } catch (Exception e) { - e.printStackTrace(System.err); + logger.error("Error creating server transaction" , e); return false; } if (serverTransaction == null) diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java index dbd2bc9fc..9272bb055 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java @@ -650,6 +650,15 @@ protected void initialize(String sipAddress, new OperationSetCusaxUtilsSipImpl(this)); } + if(accountID.getAccountPropertyBoolean( + OperationSetTelephonyBLFSipImpl.BLF_ENABLED_ACC_PROP, + false)) + { + addSupportedOperationSet( + OperationSetTelephonyBLF.class, + new OperationSetTelephonyBLFSipImpl(this)); + } + //initialize our OPTIONS handler this.capabilities = new ClientCapabilities(this); diff --git a/src/net/java/sip/communicator/service/protocol/OperationSetTelephonyBLF.java b/src/net/java/sip/communicator/service/protocol/OperationSetTelephonyBLF.java new file mode 100644 index 000000000..213c3b40c --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/OperationSetTelephonyBLF.java @@ -0,0 +1,147 @@ +/* + * Jitsi, 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.*; + +/** + * Provides operations necessary to monitor line activity and pickup calls + * if needed. BLF stands for Busy Lamp Field. + * @author Damian Minkov + */ +public interface OperationSetTelephonyBLF + extends OperationSet +{ + /** + * Adds BLFStatus listener + * @param listener the listener to add. + */ + public void addStatusListener(BLFStatusListener listener); + + /** + * Removes BLFStatus listener. + * @param listener the listener to remove. + */ + public void removeStatusListener(BLFStatusListener listener); + + /** + * To pickup the call for the monitored line if possible. + * + * @param line to try to pick up. + * + * @throws OperationFailedException if line address is not valid. + */ + public void pickup(Line line) + throws OperationFailedException; + + /** + * List of currently monitored lines. + * @return list of currently monitored lines. + */ + public List getCurrentlyMonitoredLines(); + + /** + * The monitored line. + */ + public static class Line + extends DataObject + { + /** + * The address of the line. + */ + private String address; + + /** + * The display name of the line. + */ + private String name; + + /** + * The group under witch to display the line. + */ + private String group; + + /** + * The parent provider. + */ + private ProtocolProviderService provider; + + /** + * Constructs Line. + * + * @param address the address of the line. + * @param name the display name if any + * @param group the group name if any + * @param provider the parent provider. + */ + public Line(String address, String name, String group, + ProtocolProviderService provider) + { + this.address = address; + this.name = name; + this.group = group; + this.provider = provider; + } + + /** + * The address of the line. + * @return address of the line. + */ + public String getAddress() + { + return address; + } + + /** + * The name of the line. + * @return the name of the line. + */ + public String getName() + { + return name; + } + + /** + * The group name. + * @return the group name. + */ + public String getGroup() + { + return group; + } + + /** + * The provider. + * @return the provider. + */ + public ProtocolProviderService getProvider() + { + return provider; + } + + @Override + public boolean equals(Object o) + { + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + + Line line = (Line) o; + + return address.equals(line.address); + + } + + @Override + public int hashCode() + { + return address.hashCode(); + } + } +} diff --git a/src/net/java/sip/communicator/service/protocol/event/BLFStatusEvent.java b/src/net/java/sip/communicator/service/protocol/event/BLFStatusEvent.java new file mode 100644 index 000000000..d6e964e2c --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/BLFStatusEvent.java @@ -0,0 +1,91 @@ +/* + * Jitsi, 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; + +import java.util.*; + +/** + * The status event used when Busy Lamp Field state changes. + * @author Damian Minkov + */ +public class BLFStatusEvent + extends EventObject +{ + /** + * Indicates that the BLFStatusEvent instance was triggered + * by the change of the line to offline. + */ + public static final int STATUS_OFFLINE = 0; + + /** + * Indicates that the BLFStatusEvent instance was triggered + * by the change of the line to ringing or setting up the call. + */ + public static final int STATUS_RINGING = 1; + + /** + * Indicates that the BLFStatusEvent instance was triggered + * by the change of the line to busy, someone is on the phone. + */ + public static final int STATUS_BUSY = 2; + + /** + * Indicates that the BLFStatusEvent instance was triggered + * by the change of the line to available, free, no one is using it. + */ + public static final int STATUS_FREE = 3; + + /** + * The type of the event. + */ + private int type = STATUS_OFFLINE; + + /** + * Constructs a BLFStatus event. + * + * @param source The object on which the Event initially occurred. + * @param type the event type + * + * @throws IllegalArgumentException if source is null. + */ + public BLFStatusEvent(Object source, + int type) + { + super(source); + this.type = type; + } + + /** + * Returns the type of the event. + * @return the type of the event. + */ + public int getType() + { + return type; + } + + @Override + public String toString() + { + String statusName = null; + switch(type) + { + case STATUS_OFFLINE : + statusName = "Offline"; break; + case STATUS_RINGING : + statusName = "Ringing"; break; + case STATUS_BUSY : + statusName = "Busy"; break; + case STATUS_FREE : + statusName = "Free"; break; + } + return "BLFStatusEvent{" + + "type=" + type + + ", name=" + statusName + + '}'; + } +} diff --git a/src/net/java/sip/communicator/service/protocol/event/BLFStatusListener.java b/src/net/java/sip/communicator/service/protocol/event/BLFStatusListener.java new file mode 100644 index 000000000..dd5adbe3f --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/BLFStatusListener.java @@ -0,0 +1,25 @@ +/* + * Jitsi, 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; + +import java.util.*; + +/** + * Listens for changes in the BLFStatus of the monitored lines we have + * subscribed for. + * @author Damian Minkov + */ +public interface BLFStatusListener + extends EventListener +{ + /** + * Called whenever a change occurs in the BLFStatus of one of the + * monitored lines that we have subscribed for. + * @param event the BLFStatusEvent describing the status change. + */ + public void blfStatusChanged(BLFStatusEvent event); +}