git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1047 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
mguessan 2010-05-13 12:44:21 +00:00
parent 335402a52d
commit 437837e21c
13 changed files with 1204 additions and 0 deletions

70
libgrowl/build.xml Normal file
View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="UpdateTesting" default="build">
<property name="build.dir" value="build" />
<property name="classes.dir" value="${build.dir}/classes" />
<property name="target.dir" value="${build.dir}/dist" />
<property name="javadoc.dir" value="${build.dir}/apidocs" />
<property name="src.java.dir" value="src/java" />
<property name="src.native.dir" value="src/native" />
<property name="src.resources.dir" value="src/resources" />
<target name="build-java">
<mkdir dir="${classes.dir}" />
<javac debug="true" destdir="${classes.dir}" srcdir="${src.java.dir}" />
</target>
<target name="build-headers" depends="build-java">
<javah destdir="${src.native.dir}" classpath="${classes.dir}">
<class name="info.growl.GrowlNative" />
</javah>
</target>
<target name="build-native">
<exec executable="make" dir="${src.native.dir}" failonerror="yes" />
<copy todir="${target.dir}">
<fileset dir="${src.native.dir}" includes="**/*.jnilib" />
</copy>
</target>
<target name="copy-resources">
<copy todir="${classes.dir}">
<fileset dir="${src.resources.dir}" includes="**/*" />
</copy>
</target>
<target name="build-jar" depends="build-java, copy-resources">
<mkdir dir="${target.dir}" />
<jar destfile="${target.dir}/Growl.jar" basedir="${classes.dir}">
</jar>
</target>
<target name="build" depends="build-jar,build-headers,build-native" />
<target name="javadoc" depends="build-jar">
<mkdir dir="${javadoc.dir}" />
<javadoc sourcepath="${src.java.dir}" destdir="${javadoc.dir}"
author="yes" header="Growl Java APIs" />
</target>
<target name="clean">
<delete dir="${build.dir}" />
<delete>
<fileset dir="${src.native.dir}" includes="**/info_growl_*.h" />
</delete>
<exec executable="make" dir="${src.native.dir}" failonerror="yes">
<arg line="clean" />
</exec>
</target>
<target name="run" depends="build">
<java classname="info.growl.test.TestGrowl" fork="true" dir="${target.dir}" classpath="${target.dir}/Growl.jar">
<jvmarg line="-Djava.library.path=."/>
</java>
</target>
</project>

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2008, Michael Stringer
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Growl nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY <copyright holder> ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package info.growl;
import java.awt.image.RenderedImage;
/**
* Dummy implementation of the Growl API for situations where the native code
* cannot be loaded (non-Mac systems).
*
* @author Michael Stringer
* @version 0.1
*/
class DummyGrowl implements Growl {
/**
* {@inheritDoc}
*/
public void addNotification(String name, boolean enabledByDefault) {
// does nothing
}
/**
* {@inheritDoc}
*/
public void register() throws GrowlException {
// does nothing
}
/**
* {@inheritDoc}
*/
public void sendNotification(String name, String title, String body)
throws GrowlException {
// does nothing
}
/**
* {@inheritDoc}
*/
public void sendNotification(String name, String title, String body,
RenderedImage icon) throws GrowlException {
// does nothing
}
/**
* {@inheritDoc}
*/
public void setIcon(RenderedImage icon) throws GrowlException {
// does nothing
}
/**
* {@inheritDoc}
*/
public void sendNotification(String name, String title, String body,
String callbackContext) throws GrowlException {
// does nothing
}
/**
* {@inheritDoc}
*/
public void sendNotification(String name, String title, String body,
String callbackContext, RenderedImage icon) throws GrowlException {
// does nothing
}
/**
* {@inheritDoc}
*/
public void addCallbackListener(GrowlCallbackListener listener) {
// does nothing
}
}

View File

@ -0,0 +1,160 @@
/*
* Copyright (c) 2008, Michael Stringer
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Growl nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY <copyright holder> ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package info.growl;
import java.awt.image.RenderedImage;
/**
* Interface for sending notifications to Growl.
*
* @author Michael Stringer
* @version 0.1
*/
public interface Growl {
/**
* Registers this Growl object with the Growl service.
*
* @throws GrowlException
* If an error occurs during the registration.
*/
public void register() throws GrowlException;
/**
* Sets the icon to display for notifications from this <code>Growl</code>.
*
* This <b>must</b> be called before calling {@link #register()}. If the
* icon is changed after {@link #register()} has been called then another
* call to {@link #register()} must be made.
*
* @param icon
* The icon to display.
* @throws GrowlException
* If an error occurs while setting the icon.
*/
public void setIcon(RenderedImage icon) throws GrowlException;
/**
* Adds a notification type for this <code>Growl</code>. This <b>must</b>
* be called before calling {@link #register()}. If the another
* notification type is added after {@link #register()} has been called then
* another call to {@link #register()} must be made.
*
* @param name
* The name of the notification type. This is what appears in
* the Growl settings.
* @param enabledByDefault
* <code>true</code> if this notification type should be
* enabled by default. This can be overridden by the user in
* the Growl settings.
*/
public void addNotification(String name, boolean enabledByDefault);
/**
* Adds a click callback listener.
*
* @param listener
* The callback listener to add.
*/
public void addCallbackListener(GrowlCallbackListener listener);
/**
* Sends a notification to Growl for displaying. This <b>must</b> be called
* after calling {@link #register()}.
*
* @param name
* Name of the notification type that has been registered.
* @param title
* The title of the notification.
* @param body
* The body of the notification.
* @throws GrowlException
* If the notification could not be sent.
*/
public void sendNotification(String name, String title, String body)
throws GrowlException;
/**
* Sends a notification to Growl for displaying. This <b>must</b> be called
* after calling {@link #register()}.
*
* @param name
* Name of the notification type that has been registered.
* @param title
* The title of the notification.
* @param body
* The body of the notification.
* @param icon
* The icon to display with the notification.
* @throws GrowlException
* If the notification could not be sent.
*/
public void sendNotification(String name, String title, String body,
RenderedImage icon) throws GrowlException;
/**
* Sends a notification to Growl for displaying. This <b>must</b> be called
* after calling {@link #register()}.
*
* @param name
* Name of the notification type that has been registered.
* @param title
* The title of the notification.
* @param body
* The body of the notification.
* @param callbackContext
* A unique ID that will be sent to any registered
* {@link GrowlCallbackListener}s. If this is
* <code>null</code> then clicks will be ignored.
* @throws GrowlException
* If the notification could not be sent.
*/
public void sendNotification(String name, String title, String body,
String callbackContext) throws GrowlException;
/**
* Sends a notification to Growl for displaying. This <b>must</b> be called
* after calling {@link #register()}.
*
* @param name
* Name of the notification type that has been registered.
* @param title
* The title of the notification.
* @param body
* The body of the notification.
* @param callbackContext
* A unique ID that will be sent to any registered
* {@link GrowlCallbackListener}s. If this is
* <code>null</code> then clicks will be ignored.
* @param icon
* The icon to display with the notification.
* @throws GrowlException
* If the notification could not be sent.
*/
public void sendNotification(String name, String title, String body,
String callbackContext, RenderedImage icon) throws GrowlException;
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2008, Michael Stringer
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Growl nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY <copyright holder> ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package info.growl;
/**
* Callback interface for handling users clicking on notifications.
*
* @author Michael Stringer
* @version 0.1
*/
public interface GrowlCallbackListener {
/**
* Signals that the user has clicked on a notification with an identifier of
* <code>clickContext</code>.
*
* @param clickContext
* The ID of the notification clicked.
*/
public void notificationWasClicked(String clickContext);
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2008, Michael Stringer
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Growl nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY <copyright holder> ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package info.growl;
/**
* An exception class for handling errors while sending messages to Growl.
*
* @author Michael Stringer
* @version 0.1
*/
public class GrowlException extends Exception {
private static final long serialVersionUID = 2642457707267962686L;
/**
* Creates a new <code>GrowlException</code>.
*
* @param message
* The error message.
*/
public GrowlException(String message) {
super(message);
}
/**
* Creates a new <code>GrowlException</code>.
*
* @param message
* The error message.
* @param cause
* The underlying cause.
*/
public GrowlException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,211 @@
/*
* Copyright (c) 2008, Michael Stringer
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Growl nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY <copyright holder> ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package info.growl;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
/**
* Growl notification implementation. This uses JNI to send messages to Growl.
*
* @author Michael Stringer
* @version 0.1
*/
class GrowlNative implements Growl {
private String appName;
private List<NotificationType> notifications;
private List<GrowlCallbackListener> callbackListeners;
private byte[] imageData;
private native void sendNotification(String appName, String name,
String title, String message, String callbackContext, byte[] icon);
private native void registerApp(String appName, byte[] image,
List<NotificationType> notifications);
/**
* Creates a new <code>GrowlNative</code> instance for the specified
* application name.
*
* @param appName
* The name of the application sending notifications.
*/
GrowlNative(String appName) {
notifications = new ArrayList<NotificationType>();
callbackListeners = new ArrayList<GrowlCallbackListener>();
this.appName = appName;
}
void fireCallbacks(String callbackContext) {
for (GrowlCallbackListener listener : callbackListeners) {
listener.notificationWasClicked(callbackContext);
}
}
/**
* {@inheritDoc}
*/
public void register() throws GrowlException {
registerApp(appName, imageData, notifications);
}
/**
* {@inheritDoc}
*/
public void addNotification(String name, boolean enabledByDefault) {
notifications.add(new NotificationType(name, enabledByDefault));
}
/**
* {@inheritDoc}
*/
public void addCallbackListener(GrowlCallbackListener listener) {
callbackListeners.add(listener);
}
/**
* {@inheritDoc}
*/
public void setIcon(RenderedImage icon) throws GrowlException {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(icon, "png", baos);
imageData = baos.toByteArray();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* {@inheritDoc}
*/
public void sendNotification(String name, String title, String body)
throws GrowlException {
if (!notifications.contains(new NotificationType(name, false))) {
System.out.println("contains: " + notifications);
throw new GrowlException("Unregistered notification name [" + name
+ "]");
}
sendNotification(appName, name, title, body, null, null);
}
/**
* {@inheritDoc}
*/
public void sendNotification(String name, String title, String body,
RenderedImage icon) throws GrowlException {
if (!notifications.contains(new NotificationType(name, false))) {
System.out.println("contains: " + notifications);
throw new GrowlException("Unregistered notification name [" + name
+ "]");
}
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(icon, "png", baos);
byte[] image = baos.toByteArray();
sendNotification(appName, name, title, body, null, image);
} catch (IOException ioe) {
throw new GrowlException("Failed to convert Image", ioe);
}
}
/**
* {@inheritDoc}
*/
public void sendNotification(String name, String title, String body,
String callbackContext) throws GrowlException {
if (!notifications.contains(new NotificationType(name, false))) {
System.out.println("contains: " + notifications);
throw new GrowlException("Unregistered notification name [" + name
+ "]");
}
sendNotification(appName, name, title, body, callbackContext, null);
}
/**
* {@inheritDoc}
*/
public void sendNotification(String name, String title, String body,
String callbackContext, RenderedImage icon) throws GrowlException {
if (!notifications.contains(new NotificationType(name, false))) {
System.out.println("contains: " + notifications);
throw new GrowlException("Unregistered notification name [" + name
+ "]");
}
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(icon, "png", baos);
byte[] image = baos.toByteArray();
sendNotification(appName, name, title, body, callbackContext, image);
} catch (IOException ioe) {
throw new GrowlException("Failed to convert Image", ioe);
}
}
private class NotificationType {
private String name;
private boolean enabledByDefault;
public NotificationType(String name, boolean enabledByDefault) {
this.name = name;
this.enabledByDefault = enabledByDefault;
}
public String getName() {
return name;
}
public boolean isEnabledByDefault() {
return enabledByDefault;
}
public boolean equals(Object other) {
if (!(other instanceof NotificationType)) {
return false;
}
NotificationType otherType = (NotificationType) other;
return name.equals(otherType.name);
}
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2008, Michael Stringer
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Growl nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY <copyright holder> ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package info.growl;
import java.util.HashMap;
import java.util.Map;
/**
* Utility class for sending notifications using Growl.
*
* @author Michael Stringer
* @version 0.1
*/
public final class GrowlUtils {
private static final boolean GROWL_LOADED;
private static Map<String, Growl> instances = new HashMap<String, Growl>();
static {
boolean loaded = false;
try {
System.loadLibrary("growl");
loaded = true;
} catch (UnsatisfiedLinkError ule) {
System.out.println("Failed to load Growl library");
}
GROWL_LOADED = loaded;
}
/**
* Utility method - should not be instantiated.
*/
private GrowlUtils() {
}
/**
* Gets a <code>Growl</code> instance to use for the specified application
* name. Multiple calls to this method will return the same instance.
*
* @param appName
* The name of the application.
* @return The <code>Growl</code> instance to use.
*/
public static Growl getGrowlInstance(String appName) {
Growl instance = instances.get(appName);
if (instance == null) {
if (GROWL_LOADED) {
instance = new GrowlNative(appName);
} else {
instance = new DummyGrowl();
}
instances.put(appName, instance);
}
return instance;
}
/**
* Gets whether messages can be sent to Growl. If this returns
* <code>false</code> then {@link #getGrowlInstance(String)} will return a
* dummy object.
*
* @return <code>true</code> if messages can be sent to Growl.
*/
public static boolean isGrowlLoaded() {
return GROWL_LOADED;
}
}

View File

@ -0,0 +1,186 @@
/*
* Copyright (c) 2008, Michael Stringer
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Growl nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY <copyright holder> ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package info.growl.test;
import info.growl.Growl;
import info.growl.GrowlCallbackListener;
import info.growl.GrowlException;
import info.growl.GrowlUtils;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
/**
* Simple test class for Growl.
*
* @author Michael Stringer
* @version 0.1
*/
public class TestGrowl extends JFrame {
private static final String APP_NAME = "Test Java App";
private static final String NOTIF_3_CALLBACK = "Notif3";
public TestGrowl() {
super("Growl for Java");
setSize(320, 290);
buildComponents();
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void buildComponents() {
getContentPane().setLayout(new GridLayout(6, 1));
Action action = new AbstractAction("Register") {
public void actionPerformed(ActionEvent ae) {
try {
Growl growl = GrowlUtils.getGrowlInstance(APP_NAME);
growl.addNotification("Test Notification 1", true);
growl.addNotification("Test Notification 2", false);
growl.addNotification("Test Notification 3", true);
GrowlCallbackListener listener = new GrowlCallbackListener() {
public void notificationWasClicked(
final String clickContext) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (NOTIF_3_CALLBACK.equals(clickContext)) {
JOptionPane
.showMessageDialog(
TestGrowl.this,
"User clicked on 'Test Notification 3'");
}
}
});
}
};
growl.addCallbackListener(listener);
growl.register();
} catch (GrowlException ge) {
ge.printStackTrace();
}
}
};
getContentPane().add(new JButton(action));
action = new AbstractAction("Send 'Test Notification 1'") {
public void actionPerformed(ActionEvent ae) {
try {
Growl growl = GrowlUtils.getGrowlInstance(APP_NAME);
growl.sendNotification("Test Notification 1",
"Test Notif 1", "This is a test");
} catch (GrowlException ge) {
ge.printStackTrace();
}
}
};
getContentPane().add(new JButton(action));
action = new AbstractAction("Send 'Test Notification 2'") {
public void actionPerformed(ActionEvent ae) {
try {
Growl growl = GrowlUtils.getGrowlInstance(APP_NAME);
BufferedImage image = ImageIO.read(TestGrowl.class
.getResource("/images/duke.gif"));
growl.sendNotification("Test Notification 2",
"Test Notif 2", "This is another test", image);
} catch (GrowlException ge) {
ge.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
getContentPane().add(new JButton(action));
action = new AbstractAction("Test Callback 'Notification 3'") {
public void actionPerformed(ActionEvent ae) {
try {
Growl growl = GrowlUtils.getGrowlInstance(APP_NAME);
growl.sendNotification("Test Notification 3",
"Callback Test", "Click me - I dares you!", NOTIF_3_CALLBACK);
} catch (GrowlException ge) {
ge.printStackTrace();
}
}
};
getContentPane().add(new JButton(action));
action = new AbstractAction("Reg & Test App 2") {
public void actionPerformed(ActionEvent ae) {
try {
Growl growl = GrowlUtils.getGrowlInstance("Other App");
growl.addNotification("A Notification", true);
BufferedImage image = ImageIO.read(TestGrowl.class
.getResource("/images/duke.gif"));
growl.setIcon(image);
growl.register();
growl.sendNotification("A Notification", "Testin",
"Blah de blah blah");
} catch (GrowlException ge) {
ge.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
getContentPane().add(new JButton(action));
action = new AbstractAction("Exit") {
public void actionPerformed(ActionEvent ae) {
System.exit(0);
}
};
getContentPane().add(new JButton(action));
}
public static final void main(String[] args) {
new TestGrowl();
}
}

View File

@ -0,0 +1,180 @@
#import <jni.h>
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import "info_growl_GrowlNative.h"
#import "GrowlJavaCallback.h"
jclass jListClass;
jmethodID jListSize;
jmethodID jListGet;
jclass gnClass;
jmethodID gnFireCallbacks;
jclass ntClass;
jmethodID ntGetName;
jmethodID ntIsEnabledByDefault;
jclass gcClass;
jmethodID gcNotificationWasClicked;
/*
* Convert a Java String into a Cocoa NSString.
*/
NSString *convertJavaStringToCocoa(JNIEnv *env, jstring javaString) {
NSString *cocoaString;
const char *nativeString;
nativeString = env->GetStringUTFChars(javaString, JNI_FALSE);
cocoaString = [NSString stringWithUTF8String:nativeString];
return cocoaString;
}
/**
* Initialise references to the necessary Java classes & methods.
*/
void initJavaRefs(JNIEnv *env) {
jListClass = env->FindClass("java/util/List");
jListSize = env->GetMethodID(jListClass, "size", "()I");
jListGet = env->GetMethodID(jListClass, "get", "(I)Ljava/lang/Object;");
gnClass = env->FindClass("info/growl/GrowlNative");
gnFireCallbacks = env->GetMethodID(gnClass, "fireCallbacks", "(Ljava/lang/String;)V");
ntClass = env->FindClass("info/growl/GrowlNative$NotificationType");
ntGetName = env->GetMethodID(ntClass, "getName", "()Ljava/lang/String;");
ntIsEnabledByDefault = env->GetMethodID(ntClass, "isEnabledByDefault", "()Z");
gcClass = env->FindClass("info/growl/GrowlCallbackListener");
gcNotificationWasClicked = env->GetMethodID(gcClass, "notificationWasClicked", "(Ljava/lang/String;)V");
env->ExceptionDescribe();
}
/*
* Class: info_growl_GrowlNative
* Method: sendNotification
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[B)V
*/
JNIEXPORT void JNICALL Java_info_growl_GrowlNative_sendNotification
(JNIEnv *env, jobject, jstring jAppName, jstring jNotifName, jstring jTitle, jstring jBody,
jstring jCallbackId, jbyteArray jImage) {
NSAutoreleasePool *releasePool = [[NSAutoreleasePool alloc] init];
NSString *appName = convertJavaStringToCocoa(env, jAppName);
NSString *notifName = convertJavaStringToCocoa(env, jNotifName);
NSString *title = convertJavaStringToCocoa(env, jTitle);
NSString *body = convertJavaStringToCocoa(env, jBody);
NSDistributedNotificationCenter *notifCenter = [NSDistributedNotificationCenter defaultCenter];
NSMutableDictionary *notifDictionary = [[NSMutableDictionary alloc] init];
// initialise Java classes/methods
initJavaRefs(env);
// build the dictionary for the notification
[notifDictionary setObject:appName forKey:@"ApplicationName"];
[notifDictionary setObject:appName forKey:@"ApplicationName"];
[notifDictionary setObject:notifName forKey:@"NotificationName"];
[notifDictionary setObject:title forKey:@"NotificationTitle"];
[notifDictionary setObject:body forKey:@"NotificationDescription"];
if (jCallbackId != NULL) {
// notification has a callback - register it
NSString *callbackId = convertJavaStringToCocoa(env, jCallbackId);
[notifDictionary setObject:callbackId forKey:@"NotificationClickContext"];
}
if (jImage != NULL) {
// notification has a custom icon - add it
jbyte *nativeImageData = env->GetByteArrayElements(jImage, NULL);
NSData *imageData = [NSData dataWithBytes:nativeImageData length:env->GetArrayLength(jImage)];
env->ReleaseByteArrayElements(jImage, nativeImageData, JNI_ABORT);
NSImage *image = [[NSImage alloc] initWithData:imageData];
[notifDictionary setObject:[image TIFFRepresentation] forKey:@"NotificationIcon"];
}
// send the notification
[notifCenter postNotificationName:@"GrowlNotification"
object:nil
userInfo:notifDictionary
deliverImmediately:true];
// clean up
[releasePool release];
}
/*
* Class: info_growl_GrowlNative
* Method: registerApp
* Signature: (Ljava/lang/String;[BLjava/util/List;)V
*/
JNIEXPORT void JNICALL Java_info_growl_GrowlNative_registerApp
(JNIEnv *env, jobject jThis, jstring jAppName, jbyteArray jImage, jobject jNotifList) {
NSAutoreleasePool *releasePool = [[NSAutoreleasePool alloc] init];
NSDistributedNotificationCenter *notifCenter = [NSDistributedNotificationCenter defaultCenter];
NSMutableDictionary *regDictionary = [[NSMutableDictionary alloc] init];
NSString *appName = convertJavaStringToCocoa(env, jAppName);
NSMutableArray *notifNames = [[NSMutableArray alloc] init];
NSMutableArray *defaultNotifs = [[NSMutableArray alloc] init];
// initialise Java classes/methods
initJavaRefs(env);
// build the dictionary for the registration
[regDictionary setObject:appName forKey:@"ApplicationName"];
// loop through the notifications and add them to the list
int notifSize = env->CallIntMethod(jNotifList, jListSize);
for (int i = 0; i < notifSize; i++) {
jobject notifType = env->CallObjectMethod(jNotifList, jListGet, i);
jstring jNotifName = (jstring)env->CallObjectMethod(notifType, ntGetName);
NSString *notifName = convertJavaStringToCocoa(env, jNotifName);
[notifNames addObject:notifName];
if (env->CallBooleanMethod(notifType, ntIsEnabledByDefault)) {
// add to default list
[defaultNotifs addObject:notifName];
}
}
[regDictionary setObject:notifNames forKey:@"AllNotifications"];
[regDictionary setObject:defaultNotifs forKey:@"DefaultNotifications"];
// register the click callback
jobject callbackRef = env->NewGlobalRef(jThis);
GrowlJavaCallback *callback = [[GrowlJavaCallback alloc] initWithCallback:callbackRef];
NSString *growlNotifName = [[NSString alloc] initWithFormat:@"%@%@",
appName, @"GrowlClicked!"];
[notifCenter addObserver:callback
selector:@selector(growlNotificationWasClicked:)
name:growlNotifName
object:nil];
if (jImage != NULL) {
// set the image for this application's registration
jbyte *nativeImageData = env->GetByteArrayElements(jImage, NULL);
NSData *imageData = [NSData dataWithBytes:nativeImageData length:env->GetArrayLength(jImage)];
env->ReleaseByteArrayElements(jImage, nativeImageData, JNI_ABORT);
NSImage *image = [[NSImage alloc] initWithData:imageData];
[regDictionary setObject:[image TIFFRepresentation] forKey:@"ApplicationIcon"];
}
// send the registration
[notifCenter postNotificationName:@"GrowlApplicationRegistrationNotification"
object:nil
userInfo:regDictionary
deliverImmediately:true];
// clean up
[releasePool release];
}

View File

@ -0,0 +1,14 @@
#import <jni.h>
@interface GrowlJavaCallback : NSObject {
jobject _callbackObject;
jclass _callbackClass;
jmethodID _callbackMethod;
}
- (id) initWithCallback : (jobject) callback;
- (void) growlNotificationWasClicked : (NSNotification *)notification;
@end

View File

@ -0,0 +1,51 @@
#import <jni.h>
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import "GrowlJavaCallback.h"
static JavaVM *cachedJVM = NULL;
@implementation GrowlJavaCallback
- (id) initWithCallback : (jobject) callback {
self = [super init];
_callbackObject = callback;
return self;
}
- (void) growlNotificationWasClicked : (NSNotification *)notification {
// get the callback id
NSDictionary *callbackDictionary = (NSDictionary *)[notification userInfo];
NSString *callbackId = (NSString *)[callbackDictionary objectForKey:@"ClickedContext"];
const char *nativeString;
jstring convertedString;
if (cachedJVM == NULL) {
jsize jvmCount;
jint jvmError = JNI_GetCreatedJavaVMs(&cachedJVM, 1, &jvmCount);
if (jvmError != 0) {
// TODO panic like crazy
NSLog(@"Error error error");
}
}
if (cachedJVM != NULL) {
JNIEnv *env = NULL;
cachedJVM->GetEnv((void **)&env, JNI_VERSION_1_2);
_callbackClass = env->GetObjectClass(_callbackObject);
_callbackMethod = env->GetMethodID(_callbackClass, "fireCallbacks", "(Ljava/lang/String;)V");
nativeString = [callbackId UTF8String];
convertedString = env->NewStringUTF(nativeString);
// fire off the callback
env->CallVoidMethod(_callbackObject, _callbackMethod, convertedString);
}
}
@end

View File

@ -0,0 +1,30 @@
CXX = g++
CXXFLAGS = -W -Wall -c -I/System/Library/Frameworks/JavaVM.framework/Headers -I/System/Library/Frameworks/AppKit.framework/Headers -I/System/Library/Frameworks/Foundation.framework/Headers -isysroot /Developer/SDKs/MacOSX10.5.sdk -arch i386 -arch ppc -arch x86_64 -mmacosx-version-min=10.4
MMFLAGS = -Wno-protocol -Wundeclared-selector
LDFLAGS = -dynamiclib -isysroot /Developer/SDKs/MacOSX10.5.sdk -arch i386 -arch ppc -arch x86_64 -mmacosx-version-min=10.4
LDSUFFIXES = -framework JavaVM -framework Foundation -framework AppKit
SO_NAME = libgrowl.jnilib
.SUFFIXES: .o .mm
.mm.o:
$(CXX) $(MMFLAGS) $(CXXFLAGS) $<
MMSRCS = GrowlJava.mm \
GrowlJavaCallback.mm
.PHONY: all clean
all: $(SO_NAME)
SO_o = $(MMSRCS:.mm=.o)
.cpp.o:
$(CXX) $(CXXFLAGS) $(INCLUDES) $(EXTRA_INCLUDES) -o $@ $<
$(SO_NAME): $(SO_o)
$(CXX) $(LDFLAGS) $(LIBS) $(SO_o) -o $@ $(LDSUFFIXES)
clean:
rm -f *.jnilib
rm -f *.o

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB