From 30a05992c758b94e35c2e987b45190c00e26998c Mon Sep 17 00:00:00 2001 From: Emil Ivov Date: Wed, 13 Dec 2006 22:46:21 +0000 Subject: [PATCH] Committing Romain's contribution - an implementation of a Growl notifier for Mac OS X --- build.xml | 35 +- lib/growl.jar | Bin 0 -> 2684 bytes .../macosx/felix.client.run.properties | 98 +++++ .../GrowlNotificationActivator.java | 44 +++ .../GrowlNotificationServiceImpl.java | 344 ++++++++++++++++++ .../growlnotification.manifest.mf | 9 + 6 files changed, 527 insertions(+), 3 deletions(-) create mode 100644 lib/growl.jar create mode 100644 resources/install/macosx/felix.client.run.properties create mode 100644 src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationActivator.java create mode 100644 src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationServiceImpl.java create mode 100644 src/net/java/sip/communicator/impl/growlnotification/growlnotification.manifest.mf diff --git a/build.xml b/build.xml index d93b26e86..8a6e99813 100644 --- a/build.xml +++ b/build.xml @@ -8,6 +8,9 @@ + + + @@ -120,6 +123,9 @@ + + + @@ -293,6 +299,13 @@ + + + + + + + @@ -335,6 +348,7 @@ infostring="SIP Communicator" bundleid="org.sip-communicator" stubfile="${macosx.stubfile}" + extraclasspath="/System/Library/Java" workingdirectory="$APP_PACKAGE/Contents/Resources/Java"> @@ -353,14 +367,18 @@ + + - + + + @@ -650,7 +668,8 @@ bundle-contactlist,meta-contactlist,meta-contactlist-slick, bundle-plugin-icqaccregwizz,bundle-plugin-jabberaccregwizz, bundle-plugin-msnaccregwizz,bundle-plugin-sipaccregwizz, - bundle-version,bundle-version-impl,bundle-shutdown"/> + bundle-version,bundle-version-impl,bundle-shutdown, + bundle-growlnotification"/> @@ -1116,4 +1135,14 @@ javax.swing.event, javax.swing.border"/> prefix="net/java/sip/communicator/impl/shutdown"/> + + + + + + + + diff --git a/lib/growl.jar b/lib/growl.jar new file mode 100644 index 0000000000000000000000000000000000000000..f2ebec2f4ac326617c0509b1648ef815f1860d7f GIT binary patch literal 2684 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 literal 0 HcmV?d00001 diff --git a/resources/install/macosx/felix.client.run.properties b/resources/install/macosx/felix.client.run.properties new file mode 100644 index 000000000..7713350f7 --- /dev/null +++ b/resources/install/macosx/felix.client.run.properties @@ -0,0 +1,98 @@ +# +# Framework config properties. +# +org.osgi.framework.system.packages= org.osgi.framework; \ + javax.swing; \ + javax.swing.event; \ + javax.swing.table; \ + javax.swing.text; \ + javax.swing.text.html; \ + javax.accessibility; \ + javax.swing.plaf; \ + javax.swing.plaf.metal; \ + javax.swing.plaf.basic; \ + javax.imageio; \ + javax.swing.tree; \ + javax.swing.undo; \ + javax.swing.event; \ + javax.swing.border; \ + javax.swing.filechooser; \ + org.w3c.dom; \ + org.xml.sax; \ + javax.xml.parsers;\ + org.apache.xml.serializer; \ + javax.xml.transform; \ + javax.xml.transform.dom; \ + javax.xml.transform.stream; \ + sun.security.action; \ + javax.net.ssl; \ + javax.naming; \ + javax.naming.directory; \ + javax.sound;\ + javax.sound.sampled; \ + edu.stanford.ejalbert; \ + edu.stanford.ejalbert.exception; \ + edu.stanford.ejalbert.exceptionhandler; \ + com.growl; + +felix.auto.start.1= reference:file:lib/bundle/org.apache.felix.servicebinder-0.8.0-SNAPSHOT.jar +#reference:file:lib/bundle/org.apache.felix.bundlerepository-0.8.0-SNAPSHOT.jar +#\ +# file:lib/bundle/shell.jar \ +# \ +# file:lib/bundle/servicebinder.jar \ +# file:lib/bundle/tablelayout.jar + +felix.auto.start.2= \ + reference:file:sc-bundles/util.jar + +felix.auto.start.3= \ + reference:file:sc-bundles/configuration.jar \ + reference:file:sc-bundles/version.jar \ + reference:file:sc-bundles/version-impl.jar \ + reference:file:sc-bundles/fileaccess.jar \ + reference:file:sc-bundles/protocol.jar \ + reference:file:sc-bundles/contactlist.jar \ + reference:file:sc-bundles/media.jar \ + reference:file:sc-bundles/protocol-icq.jar \ + reference:file:sc-bundles/protocol-sip.jar \ + reference:file:sc-bundles/protocol-jabber.jar \ + reference:file:sc-bundles/protocol-msn.jar \ + reference:file:sc-bundles/netaddr.jar \ + reference:file:sc-bundles/meta-cl.jar + +felix.auto.start.4= \ + reference:file:sc-bundles/history.jar \ + reference:file:sc-bundles/msghistory.jar \ + reference:file:sc-bundles/callhistory.jar + + + felix.auto.start.66= \ + reference:file:sc-bundles/swing-ui.jar \ + reference:file:sc-bundles/growlnotification.jar + + felix.auto.start.67= \ + reference:file:sc-bundles/icqaccregwizz.jar \ + reference:file:sc-bundles/sipaccregwizz.jar \ + reference:file:sc-bundles/jabberaccregwizz.jar \ + reference:file:sc-bundles/msnaccregwizz.jar \ + reference:file:sc-bundles/shutdown.jar + +# Uncomment the following lines if you want to run the architect viewer +# bundle. +#oscar.auto.start.100= \ +# file:lib/bundle/architectureviewer1.1.jar + +#Specify the directory where oscar should deploy its bundles +felix.cache.profiledir=sip-communicator.bin + + +felix.startlevel.framework=100 +felix.startlevel.bundle=100 +# +# Bundle config properties. +# +#org.osgi.service.http.port=8080 +#osgi.shell.telnet=on +#oscar.repository.url=file:/home/rickhall/projects/noscar/repository.xml +oscar.embedded.execution=false diff --git a/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationActivator.java b/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationActivator.java new file mode 100644 index 000000000..3fbfb6b77 --- /dev/null +++ b/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationActivator.java @@ -0,0 +1,44 @@ +/* + * 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.growlnotification; + +import org.osgi.framework.*; +import net.java.sip.communicator.util.*; + +/** + * Activates the GrowlNotificationService + * + * @author Romain Kuntz + */ +public class GrowlNotificationActivator + implements BundleActivator +{ + private static Logger logger = + Logger.getLogger(GrowlNotificationActivator.class); + + private GrowlNotificationServiceImpl growlNotificationService = null; + + /** + * Initialize and start Growl Notifications Service + * + * @param bundleContext BundleContext + * @throws Exception + */ + public void start(BundleContext bundleContext) throws Exception + { + /* Create and start the Growl Notification service. */ + growlNotificationService = new GrowlNotificationServiceImpl(); + growlNotificationService.start(bundleContext); + + logger.info("Growl Notification Plugin ...[Started]"); + } + + public void stop(BundleContext bundleContext) throws Exception + { + logger.info("Growl Notification Service ...[Stopped]"); + } +} diff --git a/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationServiceImpl.java b/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationServiceImpl.java new file mode 100644 index 000000000..0bc0b5b80 --- /dev/null +++ b/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationServiceImpl.java @@ -0,0 +1,344 @@ +/* + * 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.growlnotification; + +import java.util.*; + +import org.osgi.framework.*; +import com.growl.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import java.lang.reflect.*; + + +/** + * The Growl Notification Service displays on-screen information such as + * messages or call received, etc. + * + * @author Romain Kuntz + */ +public class GrowlNotificationServiceImpl + implements MessageListener, + ServiceListener +{ + /** + * The logger for this class. + */ + private static Logger logger = + Logger.getLogger(GrowlNotificationServiceImpl.class); + + /** + * The BundleContext that we got from the OSGI bus. + */ + private BundleContext bundleContext = null; + + /** + * The Growl notifier + */ + private Growl notifier; + + /** + * The noifyGrowlOf method of the growl class. We use reflection to access + * it in order to avoid compilation errors on non mac platforms. + */ + private Method notifyMethod = null; + + /* All Growl Notifications and the default ones */ + private String [] allNotif = + new String[] { "SIP Communicator Started", + "Protocol events", + "Message Received", + "Message Sent"}; + + private String [] defaultNotif = + new String[] { "SIP Communicator Started", + "Message Received" }; + + /** + * starts the service. Creates a Growl notifier, and check the current + * registerd protocol providers which supports BasicIM and adds message + * listener to them. + * + * @param bc a currently valid bundle context + * @throws java.lang.Exception if we fail initializing the growl notifier. + */ + public void start(BundleContext bc) + throws Exception + { + logger.debug("Starting the Growl Notification implementation."); + this.bundleContext = bc; + + /* Register to Growl */ + try + { + Constructor constructor = Growl.class.getConstructor(new Class[] + {String.class, String.class, String.class}); + notifier = (Growl)constructor.newInstance( + new Object[]{"SIP Communicator", allNotif, defaultNotif}); + notifier.register(); + + //init the notifyGrowlOf method + notifyMethod = Growl.class.getMethod( + "notifyGrowlOf" + , new Class[]{String.class, String.class, String.class}); + + notifyGrowlOf("SIP Communicator Started" + , "Welcome to SIP Communicator" + , "http://www.sip-communicator.org"); + } + catch (Exception ex) + { + logger.error("Could not send the message to Growl", ex); + throw ex; + } + + /* Start listening for newly register or removed protocol providers */ + bc.addServiceListener(this); + + ServiceReference[] protocolProviderRefs = null; + try + { + protocolProviderRefs = bc.getServiceReferences( + ProtocolProviderService.class.getName(), + null); + } + catch (InvalidSyntaxException ex) + { + // this shouldn't happen since we're providing no parameter string + // but let's log just in case. + logger.error("Error while retrieving service refs", ex); + return; + } + + // in case we found any + if (protocolProviderRefs != null) + { + logger.debug("Found " + + protocolProviderRefs.length + + " already installed providers."); + for (int i = 0; i < protocolProviderRefs.length; i++) + { + ProtocolProviderService provider = (ProtocolProviderService) bc + .getService(protocolProviderRefs[i]); + + this.handleProviderAdded(provider); + } + } + } + + /** + * stops the service. + * + * @param bc BundleContext + */ + public void stop(BundleContext bc) + { + // start listening for newly register or removed protocol providers + bc.removeServiceListener(this); + + ServiceReference[] protocolProviderRefs = null; + try + { + protocolProviderRefs = bc.getServiceReferences( + ProtocolProviderService.class.getName(), + null); + } + catch (InvalidSyntaxException ex) + { + // this shouldn't happen since we're providing no parameter string + // but let's log just in case. + logger.error( + "Error while retrieving service refs", ex); + return; + } + + // in case we found any + if (protocolProviderRefs != null) + { + for (int i = 0; i < protocolProviderRefs.length; i++) + { + ProtocolProviderService provider = (ProtocolProviderService) bc + .getService(protocolProviderRefs[i]); + + this.handleProviderRemoved(provider); + } + } + } + + // //////////////////////////////////////////////////////////////////////// + // MessageListener implementation methods + + /** + * Passes the newly received message to growl. + * @param evt MessageReceivedEvent the vent containing the new message. + */ + public void messageReceived(MessageReceivedEvent evt) + { + try + { + notifyGrowlOf("Message Received" + , evt.getSourceContact().getDisplayName() + , evt.getSourceMessage().getContent()); + } + catch (Exception ex) + { + logger.error("Could not notify the received message to Growl", ex); + } + } + + /** + * Notify growl that a message has been sent. + * @param evt the event containing the message that has just been sent. + */ + public void messageDelivered(MessageDeliveredEvent evt) + { + try + { + notifyGrowlOf("Message Sent" + , "Me" + , evt.getSourceMessage().getContent()); + } + catch (Exception ex) + { + logger.error("Could not pass the sent message to Growl", ex); + } + } + + /** + * Currently unused + * @param evt ignored + */ + public void messageDeliveryFailed(MessageDeliveryFailedEvent evt) + { + } + // ////////////////////////////////////////////////////////////////////////// + + /** + * When new protocol provider is registered we check + * does it supports BasicIM and if so add a listener to it + * + * @param serviceEvent ServiceEvent + */ + public void serviceChanged(ServiceEvent serviceEvent) + { + Object sService + = bundleContext.getService(serviceEvent.getServiceReference()); + + logger.trace("Received a service event for: " + + sService.getClass().getName()); + + // we don't care if the source service is not a protocol provider + if (! (sService instanceof ProtocolProviderService)) + { + return; + } + + logger.debug("Service is a protocol provider."); + if (serviceEvent.getType() == ServiceEvent.REGISTERED) + { + logger.debug("Handling registration of a new Protocol Provider."); + + this.handleProviderAdded((ProtocolProviderService)sService); + } + else if (serviceEvent.getType() == ServiceEvent.UNREGISTERING) + { + this.handleProviderRemoved( (ProtocolProviderService) sService); + } + + } + + /** + * Used to attach the Growl Notification Service to existing or + * just registered protocol provider. Checks if the provider has + * implementation of OperationSetBasicInstantMessaging + * + * @param provider ProtocolProviderService + */ + private void handleProviderAdded(ProtocolProviderService provider) + { + logger.debug("Adding protocol provider " + provider.getProtocolName()); + + // check whether the provider has a basic im operation set + OperationSetBasicInstantMessaging opSetIm + = (OperationSetBasicInstantMessaging) provider + .getSupportedOperationSets().get( + OperationSetBasicInstantMessaging.class.getName()); + + if (opSetIm != null) + { + opSetIm.addMessageListener(this); + try + { + notifyGrowlOf("Protocol events" + , "New Protocol Registered" + , provider.getProtocolName() + " registered"); + } + catch (Exception ex) + { + logger.error("Could not notify the message to Growl", ex); + } + } + else + { + logger.trace("Service did not have a im op. set."); + } + } + + /** + * Removes the specified provider from the list of currently known providers + * and ignores all the messages exchanged by it + * + * @param provider the ProtocolProviderService that has been unregistered. + */ + private void handleProviderRemoved(ProtocolProviderService provider) + { + OperationSetBasicInstantMessaging opSetIm + = (OperationSetBasicInstantMessaging) provider + .getSupportedOperationSets().get( + OperationSetBasicInstantMessaging.class.getName()); + + if (opSetIm != null) + { + opSetIm.removeMessageListener(this); + try + { + notifyGrowlOf("Protocol events" + , "Protocol deregistered" + , provider.getProtocolName() + + " deregistered"); + } + catch (Exception ex) + { + logger.error("Could not notify the message to Growl", ex); + } + } + } + + /** + * Convenience method that defers to notifier.notifyGrowlOf() using + * reflection without referencing it directly. The purpose of this method + * is to allow the class to compile on non-mac systems. + * + * @param inNotificationName The name of one of the notifications we told + * growl about. + * @param inTitle The Title of our Notification as Growl will show it + * @param inDescription The Description of our Notification as Growl will + * display it + * + * @throws Exception When a notification is not known + */ + public void notifyGrowlOf(String inNotificationName, + String inTitle, + String inDescription) + throws Exception + { + notifyMethod.invoke( + notifier, new Object[]{inNotificationName, inTitle, inDescription}); + } + +} diff --git a/src/net/java/sip/communicator/impl/growlnotification/growlnotification.manifest.mf b/src/net/java/sip/communicator/impl/growlnotification/growlnotification.manifest.mf new file mode 100644 index 000000000..400db0861 --- /dev/null +++ b/src/net/java/sip/communicator/impl/growlnotification/growlnotification.manifest.mf @@ -0,0 +1,9 @@ +Bundle-Activator: net.java.sip.communicator.impl.growlnotification.GrowlNotificationActivator +Bundle-Name: Growl Notification Service Provider +Bundle-Description: A bundle that implements the Growl notification package. +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: org.osgi.framework, + net.java.sip.communicator.util, + net.java.sip.communicator.service.protocol, + net.java.sip.communicator.service.protocol.event