diff --git a/build.xml b/build.xml index 921031823..9a98d1dfc 100644 --- a/build.xml +++ b/build.xml @@ -1070,6 +1070,7 @@ bundle-notification-service,bundle-notification-handlers, bundle-notification-wiring,bundle-notification-config, bundle-contacteventhandler, + bundle-plugin-blf, bundle-plugin-contactinfo,bundle-plugin-chatalerter, bundle-keybindings, bundle-plugin-keybindingChooser,bundle-plugin-globalproxyconfig, bundle-jfontchooserlib,bundle-update,bundle-plugin-update, @@ -2753,6 +2754,15 @@ org.bouncycastle.util"/> + + + + + + + Jitsi is distributed under \ the terms of the LGPL (\ http://www.gnu.org). +plugin.busylampfield.PICKUP=Pickup call + # Dict protocol service.protocol.DICTIONARIES=Dictionaries plugin.dictaccregwizz.ANY_DICTIONARY=Any Dictionary diff --git a/src/net/java/sip/communicator/plugin/busylampfield/BLFActivator.java b/src/net/java/sip/communicator/plugin/busylampfield/BLFActivator.java new file mode 100644 index 000000000..a8b780714 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/busylampfield/BLFActivator.java @@ -0,0 +1,249 @@ +/* + * 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.plugin.busylampfield; + +import net.java.sip.communicator.service.contactsource.*; +import net.java.sip.communicator.service.customcontactactions.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; +import org.jitsi.service.configuration.*; +import org.osgi.framework.*; + +import java.util.*; + +/** + * OSGi Activator for the Busy Lamp Field Plugin. + * + * @author Damian Minkov + */ +public class BLFActivator + implements BundleActivator, + ServiceListener, + BLFStatusListener +{ + /** + * The bundle context. + */ + static BundleContext bundleContext; + + /** + * Property to enable/disable this plugin. + */ + private static final String BLF_PLUGIN_ENABLED = + "net.java.sip.communicator.plugin.busylampfield.ENABLED"; + + /** + * The name of default group name if none is present. + */ + private static final String BLF_DEFAULT_GROUP_NAME = "Monitored Lines"; + + /** + * List of currently registered contact source services for the + * corresponding group. + */ + private Map> + currentBLFGroups = new LinkedHashMap + >(); + + /** + * Starts implementation. + * + * @param bundleContext + * @throws Exception + */ + @Override + public void start(BundleContext bundleContext) + throws Exception + { + ConfigurationService config = ServiceUtils.getService( + bundleContext, ConfigurationService.class); + + if(!config.getBoolean(BLF_PLUGIN_ENABLED, false)) + return; + + BLFActivator.bundleContext = bundleContext; + + bundleContext.addServiceListener(this); + + initProviders(); + + bundleContext.registerService( + CustomContactActionsService.class.getName(), + new CustomActionsBLFSourceContact(), + null + ); + } + + /** + * Stops. + * @param bundleContext + * @throws Exception + */ + @Override + public void stop(BundleContext bundleContext) + throws Exception + {} + + /** + * Listens for registered providers. + * @param serviceEvent + */ + @Override + public void serviceChanged(ServiceEvent serviceEvent) + { + ServiceReference serviceRef = serviceEvent.getServiceReference(); + + // if the event is caused by a bundle being stopped, we don't want to + // know + if(serviceRef.getBundle().getState() == Bundle.STOPPING) + return; + + Object service = bundleContext.getService(serviceRef); + + // we don't care if the source service is not a protocol provider + if(!(service instanceof ProtocolProviderService)) + return; + + switch(serviceEvent.getType()) + { + case ServiceEvent.REGISTERED: + handleProviderAdded((ProtocolProviderService) service); + break; + case ServiceEvent.UNREGISTERING: + handleProviderRemoved((ProtocolProviderService) service); + break; + } + } + + /** + * Initializes all currently registered protocol providers that contain + * the desired operation set. + */ + private void initProviders() + { + for(ServiceReference serRef : + ServiceUtils.getServiceReferences( + bundleContext, ProtocolProviderService.class)) + { + handleProviderAdded(bundleContext.getService(serRef)); + } + } + + /** + * Adds a protocol provider, when such provider is registered. + * + * @param protocolProvider the ProtocolProviderService to add + */ + private void handleProviderAdded(ProtocolProviderService protocolProvider) + { + OperationSetTelephonyBLF opset = + protocolProvider.getOperationSet(OperationSetTelephonyBLF.class); + + if(opset == null) + return; + + opset.addStatusListener(this); + + for(OperationSetTelephonyBLF.Line line + : opset.getCurrentlyMonitoredLines()) + { + String groupName = line.getGroup(); + if(groupName == null) + groupName = BLF_DEFAULT_GROUP_NAME; + + ServiceRegistration serviceReg + = currentBLFGroups.get(groupName); + + if(serviceReg != null) + { + BLFContactSourceService css + = (BLFContactSourceService)bundleContext.getService( + serviceReg.getReference()); + css.addLine(line); + + continue; + } + else + { + BLFContactSourceService css + = new BLFContactSourceService( + groupName, currentBLFGroups.size() + 1); + + serviceReg = (ServiceRegistration) + bundleContext.registerService( + ContactSourceService.class.getName(), + css, + null); + currentBLFGroups.put(groupName, serviceReg); + + css.addLine(line); + } + } + } + + /** + * Removes a protocol provider from the list when unregistered. + * @param protocolProvider the ProtocolProviderService to remove + */ + private void handleProviderRemoved(ProtocolProviderService protocolProvider) + { + OperationSetTelephonyBLF opset = + protocolProvider.getOperationSet(OperationSetTelephonyBLF.class); + + if(opset == null) + return; + + opset.removeStatusListener(this); + + for(OperationSetTelephonyBLF.Line line + : opset.getCurrentlyMonitoredLines()) + { + String groupName = line.getGroup(); + if(groupName == null) + groupName = BLF_DEFAULT_GROUP_NAME; + + ServiceRegistration serviceReg + = currentBLFGroups.remove(groupName); + + if(serviceReg == null) + continue; + + serviceReg.unregister(); + } + } + + /** + * 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. + */ + @Override + public void blfStatusChanged(BLFStatusEvent event) + { + if(!(event.getSource() instanceof OperationSetTelephonyBLF.Line)) + return; + + OperationSetTelephonyBLF.Line line + = (OperationSetTelephonyBLF.Line)event.getSource(); + + String gr = line.getGroup(); + if(gr == null) + gr = BLF_DEFAULT_GROUP_NAME; + + ServiceRegistration serviceReg + = currentBLFGroups.get(gr); + + if(serviceReg == null) + return; + + BLFContactSourceService css + = (BLFContactSourceService)bundleContext.getService( + serviceReg.getReference()); + css.updateLineStatus(line, event.getType()); + } +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/busylampfield/BLFContactQuery.java b/src/net/java/sip/communicator/plugin/busylampfield/BLFContactQuery.java new file mode 100644 index 000000000..ef0150a20 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/busylampfield/BLFContactQuery.java @@ -0,0 +1,152 @@ +/* + * 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.plugin.busylampfield; + +import net.java.sip.communicator.service.contactsource.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; + +import java.util.*; + +/** + * The query that will update lines. + * + * @author Damian Minkov + */ +public class BLFContactQuery + extends AbstractContactQuery +{ + /** + * The query string. + */ + private String queryString; + + /** + * The query result list. + */ + private final List results; + + /** + * Constructs new query. + * + * @param parentService the parent contact source service + * @param queryString the query string + * @param contactCount contacts to process, not used currently. + */ + public BLFContactQuery( + BLFContactSourceService parentService, + Collection monitoredLines, + String queryString, int contactCount) + { + super(parentService); + + this.queryString = queryString; + this.results = new ArrayList(); + for(OperationSetTelephonyBLF.Line line : monitoredLines) + { + addLine(line, false); + } + } + + /** + * Returns the query string. + * + * @return the query string + */ + @Override + public String getQueryString() + { + return queryString; + } + + /** + * Returns the list of query results. + * + * @return the list of query results + */ + @Override + public List getQueryResults() + { + return results; + } + + /** + * Starts this BLFContactQuery. + */ + @Override + public void start() + { + for(SourceContact sc : results) + { + fireContactReceived(sc, false); + } + } + + /** + * Adds new line to display. + * @param line to display status. + * @param fireEvent whether to fire events + */ + void addLine(OperationSetTelephonyBLF.Line line, boolean fireEvent) + { + for(SourceContact sc : results) + { + BLFSourceContact blfSC = (BLFSourceContact)sc; + + if(blfSC.getLine().equals(line)) + return; + } + + BLFSourceContact sc + = new BLFSourceContact(getContactSource(), line); + results.add(sc); + + if(fireEvent) + fireContactReceived(sc, false); + } + + /** + * Updates the source contact status. + * @param line + * @param status + */ + void updateLineStatus(OperationSetTelephonyBLF.Line line, int status) + { + for(SourceContact sc : results) + { + BLFSourceContact blfSC = (BLFSourceContact)sc; + if(blfSC.getLine().equals(line)) + { + blfSC.setPresenceStatus(getPresenceStatus(status)); + fireContactChanged(blfSC); + break; + } + } + } + + /** + * Maps BLFStatusEvent.type to BLFPresenceStatus. + * @param status the staus to map. + * @return the corresponding BLFPresenceStatus. + */ + private BLFPresenceStatus getPresenceStatus(int status) + { + switch(status) + { + case BLFStatusEvent.STATUS_BUSY: + return BLFPresenceStatus.BLF_BUSY; + case BLFStatusEvent.STATUS_OFFLINE: + return BLFPresenceStatus.BLF_OFFLINE; + case BLFStatusEvent.STATUS_RINGING: + return BLFPresenceStatus.BLF_RINGING; + case BLFStatusEvent.STATUS_FREE: + return BLFPresenceStatus.BLF_FREE; + default: + return null; + } + } +} diff --git a/src/net/java/sip/communicator/plugin/busylampfield/BLFContactSourceService.java b/src/net/java/sip/communicator/plugin/busylampfield/BLFContactSourceService.java new file mode 100644 index 000000000..264758c87 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/busylampfield/BLFContactSourceService.java @@ -0,0 +1,158 @@ +/* + * 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.plugin.busylampfield; + +import net.java.sip.communicator.service.contactsource.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.Logger; +import org.jitsi.util.*; + +import java.util.*; + +/** + * The contact source service to manage query and lines. + * + * @author Damian Minkov + */ +public class BLFContactSourceService + implements ContactSourceService +{ + /** + * The Logger used by the + * ConfCallsContactSource class for logging output. + */ + private static final Logger logger + = Logger.getLogger(BLFContactSourceService.class); + + /** + * Type of a recent messages source. + */ + public static final int BLF_TYPE = CONTACT_LIST_TYPE; + + + /** + * Constants to display name ContactSource. + */ + private final String displayName; + + /** + * The latest query. + */ + private BLFContactQuery ourContactQuery = null; + + /** + * The list of lines this service will handle. + */ + private Set monitoredLines + = new HashSet(); + + /** + * The current group index. + */ + private final int index; + + /** + * Constructs. + * @param displayName + */ + public BLFContactSourceService(String displayName, int index) + { + this.displayName = displayName; + this.index = index; + } + + /** + * Returns the identifier of this contact source. Some of the common + * identifiers are defined here (For example the CALL_HISTORY identifier + * should be returned by all call history implementations of this interface) + * @return the identifier of this contact source + */ + @Override + public int getType() + { + return BLF_TYPE; + } + + /** + * Returns a user-friendly string that identifies this contact source. + * @return the display name of this contact source + */ + @Override + public String getDisplayName() + { + return displayName; + } + + /** + * Queries this search source for the given queryString. + * @param queryString the string to search for + * @return the created query + */ + @Override + public ContactQuery createContactQuery(String queryString) + { + return createContactQuery(queryString, 20); + } + + /** + * Queries this search source for the given queryString. + * + * @param queryString the string to search for + * @param contactCount the maximum count of result contacts + * @return the created query + */ + @Override + public ContactQuery createContactQuery(String queryString, int contactCount) + { + if(!StringUtils.isNullOrEmpty(queryString)) + return null; + + if(ourContactQuery == null) + ourContactQuery = new BLFContactQuery( + this, monitoredLines, queryString, contactCount); + + return ourContactQuery; + } + + /** + * Returns the index of the contact source in the result list. + * + * @return the index of the contact source in the result list + */ + @Override + public int getIndex() + { + // shift it with 2 position (recent messages-0 and chat rooms-1), + // counting from 0 + return 1 + index; + } + + /** + * Adds new line to display. + * @param line to display status. + */ + void addLine(OperationSetTelephonyBLF.Line line) + { + monitoredLines.add(line); + + if(ourContactQuery != null) + ourContactQuery.addLine(line, true); + } + + /** + * Updates the source contact status. + * @param line + * @param status + */ + void updateLineStatus(OperationSetTelephonyBLF.Line line, int status) + { + if(ourContactQuery == null) + return; + + ourContactQuery.updateLineStatus(line, status); + } +} diff --git a/src/net/java/sip/communicator/plugin/busylampfield/BLFPresenceStatus.java b/src/net/java/sip/communicator/plugin/busylampfield/BLFPresenceStatus.java new file mode 100644 index 000000000..d448be720 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/busylampfield/BLFPresenceStatus.java @@ -0,0 +1,77 @@ +/* + * 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.plugin.busylampfield; + +import net.java.sip.communicator.service.protocol.*; + +/** + * The status to display for the contact srouces. + * @author Damian Minkov + */ +public class BLFPresenceStatus + extends PresenceStatus +{ + /** + * The Online status. Indicate that the line is available and free. + */ + public static final String AVAILABLE = "Available"; + + /** + * On The Phone Chat status. + * Indicates that the line is used. + */ + public static final String BUSY = "Busy"; + + /** + * Ringing status. + * Indicates that the line is currently ringing. + */ + public static final String RINGING = "On the phone"; + + /** + * Indicates an Offline status or status with 0 connectivity. + */ + public static final String OFFLINE = "Offline"; + + /** + * The Online status. Indicate that the line is free. + */ + public static final BLFPresenceStatus BLF_FREE = new BLFPresenceStatus( + 65, AVAILABLE); + + /** + * The Offline status. Indicates the line status retrieval + * is not available. + */ + public static final BLFPresenceStatus BLF_OFFLINE = new BLFPresenceStatus( + 0, OFFLINE); + + /** + * Indicates an On The Phone status. + */ + public static final BLFPresenceStatus BLF_BUSY = new BLFPresenceStatus( + 30, BUSY); + + /** + * Indicates Ringing status. + */ + public static final BLFPresenceStatus BLF_RINGING = new BLFPresenceStatus( + 31, RINGING); + + /** + * Creates an instance of BLFPresenceStatus with the + * specified parameters. + * + * @param status the connectivity level of the new presence status + * instance + * @param statusName the name of the presence status. + */ + private BLFPresenceStatus(int status, String statusName) + { + super(status, statusName); + } +} diff --git a/src/net/java/sip/communicator/plugin/busylampfield/BLFSourceContact.java b/src/net/java/sip/communicator/plugin/busylampfield/BLFSourceContact.java new file mode 100644 index 000000000..6ec3167c2 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/busylampfield/BLFSourceContact.java @@ -0,0 +1,47 @@ +/* + * 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.plugin.busylampfield; + +import net.java.sip.communicator.service.contactsource.*; +import net.java.sip.communicator.service.protocol.*; + +import java.util.*; + +/** + * The BLF source contact. + * @author Damian Minkov + */ +public class BLFSourceContact + extends GenericSourceContact +{ + private final OperationSetTelephonyBLF.Line line; + + /** + * Initializes a new AddrBookSourceContact instance. + * + * @param contactSource the ContactSourceService which is creating + * the new instance + */ + public BLFSourceContact(ContactSourceService contactSource, + OperationSetTelephonyBLF.Line line) + { + super(contactSource, + line.getName() != null ? line.getName() : line.getAddress(), + new ArrayList()); + + this.line = line; + } + + /** + * Returns the line displayed. + * @return + */ + public OperationSetTelephonyBLF.Line getLine() + { + return line; + } +} diff --git a/src/net/java/sip/communicator/plugin/busylampfield/CustomActionsBLFSourceContact.java b/src/net/java/sip/communicator/plugin/busylampfield/CustomActionsBLFSourceContact.java new file mode 100644 index 000000000..23edae9eb --- /dev/null +++ b/src/net/java/sip/communicator/plugin/busylampfield/CustomActionsBLFSourceContact.java @@ -0,0 +1,188 @@ +/* + * 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.plugin.busylampfield; + +import net.java.sip.communicator.service.contactsource.*; +import net.java.sip.communicator.service.customcontactactions.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.resources.*; +import net.java.sip.communicator.util.*; + +import java.util.*; + +/** + * Custom acction to show pickup call button. + * + * @author Damian Minkov + */ +public class CustomActionsBLFSourceContact + implements CustomContactActionsService +{ + /** + * Our logger. + */ + private final Logger logger = Logger.getLogger( + CustomActionsBLFSourceContact.class); + + /** + * List of actions. + */ + private final List> actionsList + = new LinkedList>(); + + public CustomActionsBLFSourceContact() + { + actionsList.add(new PickupAction()); + } + + /** + * Returns the template class that this service has been initialized with + * + * @return the template class + */ + @Override + public Class getContactSourceClass() + { + return SourceContact.class; + } + + /** + * Returns all custom actions defined by this service. + * + * @return an iterator over a list of ContactActions + */ + @Override + public Iterator> getCustomContactActions() + { + return actionsList.iterator(); + } + + /** + * Returns all custom actions menu items defined by this service. + * + * @return an iterator over a list of ContactActionMenuItems + */ + @Override + public Iterator> + getCustomContactActionsMenuItems() + { + return null; + } + + /** + * Action that represents pickup button. + */ + private class PickupAction + implements ContactAction + { + /** + * Invoked when an action occurs. + * + * @param actionSource the source of the action + * @param x the x coordinate of the action + * @param y the y coordinate of the action + */ + @Override + public void actionPerformed(final SourceContact actionSource, int x, int y) + throws + OperationFailedException + { + if(!(actionSource instanceof BLFSourceContact)) + return; + + new Thread(new Runnable() + { + public void run() + { + try + { + OperationSetTelephonyBLF.Line line + = ((BLFSourceContact)actionSource).getLine(); + OperationSetTelephonyBLF opset = + line.getProvider().getOperationSet( + OperationSetTelephonyBLF.class); + opset.pickup(line); + } + catch(Exception ex) + { + logger.error("Error picking up call", ex); + } + } + }).start(); + } + + /** + * The icon used by the UI to visualize this action. + * @return the button icon. + */ + @Override + public byte[] getIcon() + { + return ResourceManagementServiceUtils.getService( + BLFActivator.bundleContext).getImageInBytes( + "plugin.busylampfield.PICKUP_CALL"); + } + + /** + * The icon used by the UI to visualize this action. + * @return the button icon. + */ + @Override + public byte[] getRolloverIcon() + { + return ResourceManagementServiceUtils.getService( + BLFActivator.bundleContext).getImageInBytes( + "plugin.busylampfield.PICKUP_CALL_ROLLOVER"); + } + + /** + * The icon used by the UI to visualize this action. + * @return the button icon. + */ + @Override + public byte[] getPressedIcon() + { + return ResourceManagementServiceUtils.getService( + BLFActivator.bundleContext).getImageInBytes( + "plugin.busylampfield.PICKUP_CALL_PRESSED"); + } + + /** + * Returns the tool tip text of the component to create for this contact + * action. + * + * @return the tool tip text of the component to create for this contact + * action + */ + @Override + public String getToolTipText() + { + return ResourceManagementServiceUtils.getService( + BLFActivator.bundleContext).getI18NString( + "plugin.busylampfield.PICKUP"); + } + + /** + * Indicates if this action is visible for the + * given actionSource. + * + * @param actionSource the action source for which we're verifying the + * action. + * @return true if the action should be visible for the given + * actionSource, false - otherwise + */ + @Override + public boolean isVisible(SourceContact actionSource) + { + if(actionSource.getPresenceStatus() + .equals(BLFPresenceStatus.BLF_RINGING)) + return true; + else + return false; + } + } +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/plugin/busylampfield/busylampfield.manifest.mf b/src/net/java/sip/communicator/plugin/busylampfield/busylampfield.manifest.mf new file mode 100644 index 000000000..91f2de6d3 --- /dev/null +++ b/src/net/java/sip/communicator/plugin/busylampfield/busylampfield.manifest.mf @@ -0,0 +1,14 @@ +Bundle-Activator: net.java.sip.communicator.plugin.busylampfield.BLFActivator +Bundle-Name: Busy Lamp Field Plugin +Bundle-Description: Contact Sources that show and control BLF operation sets +Bundle-Vendor: jitsi.org +Bundle-Version: 0.0.1 +Bundle-SymbolicName: net.java.sip.communicator.plugin.busylampfield +Import-Package: org.osgi.framework, + org.jitsi.service.configuration, + net.java.sip.communicator.service.contactsource, + net.java.sip.communicator.service.customcontactactions, + net.java.sip.communicator.service.protocol, + net.java.sip.communicator.service.protocol.event, + net.java.sip.communicator.service.resources, org.jitsi.service.resources, + net.java.sip.communicator.util, org.jitsi.util