diff --git a/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationActivator.java b/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationActivator.java index e4670ad96..8892ea6b9 100644 --- a/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationActivator.java +++ b/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationActivator.java @@ -6,7 +6,10 @@ */ package net.java.sip.communicator.impl.growlnotification; +import net.java.sip.communicator.service.configuration.*; import net.java.sip.communicator.service.resources.*; +import net.java.sip.communicator.service.systray.*; + import org.osgi.framework.*; import net.java.sip.communicator.util.*; @@ -15,6 +18,7 @@ * Activates the GrowlNotificationService * * @author Romain Kuntz + * @author Egidijus Jankauskas */ public class GrowlNotificationActivator implements BundleActivator @@ -27,11 +31,21 @@ public class GrowlNotificationActivator private static final Logger logger = Logger.getLogger(GrowlNotificationActivator.class); + /** + * A reference to the configuration service. + */ + private static ConfigurationService configService; + /** * A reference to the resource management service. */ private static ResourceManagementService resourcesService; + /** + * A reference to the Growl notification service + */ + private static GrowlNotificationServiceImpl handler; + /** * Initialize and start Growl Notifications Service * @@ -40,32 +54,51 @@ public class GrowlNotificationActivator */ public void start(BundleContext bc) throws Exception { - /* Check Java version: do not start if Java 6 */ - /* Actually, this plugin uses the Growl Java bindings which - * in turn uses the Cocoa Java bridge. Java 6 on Mac OS X is - * 64-bit only (as of 01/2008), and the Cocoa-Java bridge - * will certainly never get any 64-bit support (it has been - * deprecated). - */ - String version = System.getProperty("java.version"); - char minor = version.charAt(2); - if(minor > '5') { - logger.info("Growl Notification Plugin cannot be started " + - "on JDK version " + version); - } else { - bundleContext = bc; - /* Create and start the Growl Notification service. */ - new GrowlNotificationServiceImpl().start(bc); + logger.info("Growl Notification ...[Starting]"); + bundleContext = bc; + + getConfigurationService(); - logger.info("Growl Notification Plugin ...[Started]"); + handler = new GrowlNotificationServiceImpl(); + + if (handler.isGrowlInstalled()) + { + handler.start(bc); + bc.registerService(PopupMessageHandler.class.getName(), handler, null); + } else + { + logger.info("Growl Notification ...[Aborted]"); + return; } + + logger.info("Growl Notification ...[Started]"); } public void stop(BundleContext bContext) throws Exception { + handler.stop(bContext); logger.info("Growl Notification Service ...[Stopped]"); } + /** + * Returns the ConfigurationService obtained from the bundle + * context. + * @return the ConfigurationService obtained from the bundle + * context + */ + public static ConfigurationService getConfigurationService() + { + if(configService == null) { + ServiceReference configReference = bundleContext + .getServiceReference(ConfigurationService.class.getName()); + + configService = (ConfigurationService) bundleContext + .getService(configReference); + } + + return configService; + } + /** * Returns the ResourceManagementService obtained from the bundle * context. @@ -78,6 +111,5 @@ public static ResourceManagementService getResources() resourcesService = ResourceManagementServiceUtils.getService(bundleContext); return resourcesService; - } -} +} \ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationServiceImpl.java b/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationServiceImpl.java index 8751393ad..78f2ebd83 100644 --- a/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationServiceImpl.java +++ b/src/net/java/sip/communicator/impl/growlnotification/GrowlNotificationServiceImpl.java @@ -6,182 +6,104 @@ */ package net.java.sip.communicator.impl.growlnotification; -import java.lang.reflect.*; import java.util.*; - import org.osgi.framework.*; -import com.growl.*; - import net.java.sip.communicator.service.systray.*; import net.java.sip.communicator.service.systray.event.*; import net.java.sip.communicator.util.*; +import org.growl4j.*; + +// TO-DO: use a better icon in registration. /** * The Growl Notification Service displays on-screen information such as * messages or call received, etc. * * @author Romain Kuntz + * @author Egidijus Jankauskas */ public class GrowlNotificationServiceImpl - implements PopupMessageHandler + implements PopupMessageHandler, + GrowlCallbacksListener { /** * The logger for this class. */ private static Logger logger = Logger.getLogger(GrowlNotificationServiceImpl.class); - + /** - * The Growl notifier + * A variable that acts as a buffer to temporarily keep all PopupMessages + * that were sent to Growl Daemon. */ - private Growl notifier; + private static final HashMap shownPopups = + new HashMap(10); + + private Growl growl = null; - /** - * The notifyGrowlOf/setAllowedNotifications/setDefaultNotifications - * methods of the growl class. We use reflection to access them - * in order to avoid compilation errors on non mac platforms. - */ - private Method notifyMethod = null; - private Method setAllowedNotifMethod = null; - private Method setDefaultNotifMethod = 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" }; - - /** - * The path to the SIP Communicator icon used in Growl's configuration - * menu and protocol events messages - */ - private String sipIconPath = "resources/images/logo/sc_logo_128x128.icns"; /** The list of all added popup listeners */ private final List popupMessageListeners = new Vector(); + /** - * starts the service. Creates a Growl notifier, and check the current - * registerd protocol providers which supports BasicIM and adds message + * Starts the service. Creates a Growl notifier, and check the current + * registered 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."); - - /* Register to Growl */ - try - { - Constructor constructor = Growl.class.getConstructor( - new Class[] { String.class, String.class }); - notifier = constructor.newInstance( - new Object[]{"SIP Communicator", sipIconPath}); - - //init the setAllowedNotifications method - setAllowedNotifMethod = Growl.class.getMethod( - "setAllowedNotifications" - , new Class[]{String[].class}); - - //init the setDefaultNotifications method - setDefaultNotifMethod = Growl.class.getMethod( - "setDefaultNotifications" - , new Class[]{String[].class}); - - //init the notifyGrowlOf method - notifyMethod = Growl.class.getMethod( - "notifyGrowlOf" - , new Class[]{String.class, String.class, - String.class, String.class}); - - setAllowedNotifications(allNotif); - setDefaultNotifications(defaultNotif); - notifier.register(); - - notifyGrowlOf("SIP Communicator Started" - , sipIconPath - , "Welcome to SIP Communicator" - , "http://www.sip-communicator.org"); - } - catch (Exception ex) + + if(Growl.isGrowlRunning()) { - logger.error("Could not send the message to Growl", ex); - throw ex; - } - - bc.registerService(PopupMessageHandler.class.getName(), this, null); + String[] dict = { "Default", "Welcome message" }; + byte[] sipIcon = GrowlNotificationActivator.getResources(). + getImageInBytes("service.gui.SIP_COMMUNICATOR_LOGO_45x45"); + growl = new Growl ("SIP Communicator", "net.sip-communicator", sipIcon, dict, dict); + growl.addClickedNotificationsListener(this); + + growl.notifyGrowlOf("SIP Communicator", + "http://www.sip-communicator.org/", + "Welcome message", + null, null); + } } /** - * stops the service. + * Stops the service. * * @param bc BundleContext */ public void stop(BundleContext bc) { - } - - /** - * 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 inImagePath, - String inTitle, - String inDescription) - throws Exception - { - // remove eventual html code before showing the popup message - inDescription = inDescription.replaceAll("]*+>", ""); - - notifyMethod.invoke( - notifier, new Object[]{inNotificationName, inImagePath, - inTitle, inDescription}); + if (growl != null) + { + growl.doFinalCleanUp(); + } } /** - * Convenience method that defers to notifier.setAllowedNotifications() - * using reflection without referencing it directly. The purpose of this - * method is to allow the class to compile on non-mac systems. - * - * @param inAllNotes The list of allowed Notifications + * Checks if Growl is present on the system + * @return true if Growl is installed and false otherwise */ - public void setAllowedNotifications(String [] inAllNotes) - throws Exception + public boolean isGrowlInstalled() { - setAllowedNotifMethod.invoke(notifier, new Object[]{inAllNotes}); + return Growl.isGrowlInstalled(); } - + /** - * Convenience method that defers to notifier.setDefaultNotifications() - * using reflection without referencing it directly. The purpose of this - * method is to allow the class to compile on non-mac systems. - * - * @param inDefNotes The list of default Notifications + * Checks if Growl is running + * @return true if Growl is running and false otherwise */ - public void setDefaultNotifications(String [] inDefNotes) - throws Exception + public boolean isGrowlRunning() { - setDefaultNotifMethod.invoke(notifier, new Object[]{inDefNotes}); + return Growl.isGrowlRunning(); } public void addPopupMessageListener(SystrayPopupMessageListener listener) @@ -208,19 +130,82 @@ public void removePopupMessageListener(SystrayPopupMessageListener listener) */ public void showPopupMessage(PopupMessage popupMessage) { - try - { - notifyGrowlOf("Message Received" - , sipIconPath - , popupMessage.getMessageTitle() - , popupMessage.getMessage()); + long timestamp = System.currentTimeMillis(); + synchronized(shownPopups) { + shownPopups.put(timestamp, popupMessage); } - catch (Exception ex) + + String messageBody = popupMessage.getMessage(); + String messageTitle = popupMessage.getMessageTitle(); + + // remove eventual HTML code before showing the pop-up message + messageBody = messageBody.replaceAll("]*+>", ""); + messageTitle = messageTitle.replaceAll("]*+>", ""); + + growl.notifyGrowlOf(messageTitle, + messageBody, + "Default", + popupMessage.getIcon(), + timestamp); + } + + /** + * Notifies all interested listeners that a SystrayPopupMessageEvent + * occured. + * + * @param SystrayPopupMessageEvent the evt to send to listener. + */ + private void firePopupMessageClicked(SystrayPopupMessageEvent evt) + { + logger.trace("Will dispatch the following popup event: " + evt); + + List listeners; + synchronized (popupMessageListeners) { - logger.error("Could not notify the received message to Growl", ex); + listeners = + new ArrayList( + popupMessageListeners); + } + + for (SystrayPopupMessageListener listener : listeners) + listener.popupMessageClicked(evt); + } + + /** + * This method is called by Growl when the Growl notification is not clicked + * @param context is an object that is used to identify sent notification + */ + public void growlNotificationTimedOut(Object context) + { + PopupMessage m = (PopupMessage)shownPopups.get(context); + if (m != null) { + synchronized(shownPopups) { + shownPopups.remove(context); + } + logger.trace("Growl notification timed-out :" + + m.getMessageTitle() + ": " + m.getMessage()); } + } + /** + * This method is called by Growl when the Growl notification is clicked + * @param context is an object that is used to identify sent notification + */ + public void growlNotificationWasClicked(Object context) + { + PopupMessage m = (PopupMessage)shownPopups.get(context); + if (m != null) { + synchronized(shownPopups) { + shownPopups.remove(context); + } + + firePopupMessageClicked(new SystrayPopupMessageEvent(this, m.getTag())); + logger.trace("Growl message clicked :" + + m.getMessageTitle() + ": " + m.getMessage()); + } + } + /** * Implements toString from PopupMessageHandler * @return a description of this handler @@ -230,4 +215,15 @@ public String toString() return GrowlNotificationActivator.getResources() .getI18NString("impl.growlnotification.POPUP_MESSAGE_HANDLER"); } + + /** + * Implements getPreferenceIndex from PopupMessageHandler. + * This handler is able to show images, detect clicks, match a click to a + * message, and use a native popup mechanism, thus the index is 4. + * @return a preference index + */ + public int getPreferenceIndex() + { + return 4; + } } diff --git a/src/net/java/sip/communicator/impl/growlnotification/growlnotification.manifest.mf b/src/net/java/sip/communicator/impl/growlnotification/growlnotification.manifest.mf index fb78f334b..a707c6410 100644 --- a/src/net/java/sip/communicator/impl/growlnotification/growlnotification.manifest.mf +++ b/src/net/java/sip/communicator/impl/growlnotification/growlnotification.manifest.mf @@ -5,8 +5,11 @@ Bundle-Vendor: sip-communicator.org Bundle-Version: 0.0.1 System-Bundle: yes Import-Package: org.osgi.framework, + net.java.sip.communicator.service.configuration, + net.java.sip.communicator.service.protocol, net.java.sip.communicator.util, net.java.sip.communicator.service.resources, net.java.sip.communicator.service.systray, net.java.sip.communicator.service.systray.event, - com.growl + net.java.sip.communicator.util.swing, + org.growl4j