Introduces an initial implementation of notification popups on Linux using freedesktop.org's Desktop Notifications through the native libdbus library. The committed code represents work in progress and does not support images, click reporting back to the Java code, matching notifications to Java components. That's why the introduced galagonotification bundle is built in build.xml but not started in felix.client.run.properties.

cusax-fix
Lyubomir Marinov 16 years ago
parent 14a2ffea65
commit e991bdb154

@ -799,8 +799,8 @@
bundle-plugin-msnaccregwizz,bundle-plugin-sipaccregwizz,
bundle-plugin-yahooaccregwizz,bundle-plugin-aimaccregwizz,
bundle-version,bundle-version-impl,bundle-shutdown-timeout,
bundle-growlnotification,bundle-swingnotification,bundle-sparkle,
bundle-plugin-branding,
bundle-growlnotification,bundle-swingnotification,bundle-galagonotification,
bundle-sparkle, bundle-plugin-branding,
bundle-osdependent,bundle-browserlauncher,bundle-gibberish,
bundle-gibberish-slick,bundle-plugin-gibberishaccregwizz,
bundle-plugin-call-history-form,
@ -1058,6 +1058,8 @@
prefix="net/java/sip/communicator/impl/media/codec/audio"/>
<zipfileset dir="${dest}/net/java/sip/communicator/impl/media/codec/video"
prefix="net/java/sip/communicator/impl/media/codec/video"/>
<zipfileset dir="${dest}/net/java/sip/communicator/impl/media/conference"
prefix="net/java/sip/communicator/impl/media/conference"/>
<zipfileset dir="${dest}/net/java/sip/communicator/impl/media/protocol"
prefix="net/java/sip/communicator/impl/media/protocol"/>
<zipfileset dir="${dest}/net/java/sip/communicator/impl/media/renderer"
@ -1092,6 +1094,8 @@
prefix="net/java/sip/communicator/impl/media/codec/audio"/>
<zipfileset dir="${dest}/net/java/sip/communicator/impl/media/codec/video"
prefix="net/java/sip/communicator/impl/media/codec/video"/>
<zipfileset dir="${dest}/net/java/sip/communicator/impl/media/conference"
prefix="net/java/sip/communicator/impl/media/conference"/>
<zipfileset dir="${dest}/net/java/sip/communicator/impl/media/protocol"
prefix="net/java/sip/communicator/impl/media/protocol"/>
<zipfileset dir="${dest}/net/java/sip/communicator/impl/media/renderer"
@ -1125,6 +1129,8 @@
prefix="net/java/sip/communicator/impl/media/codec/audio"/>
<zipfileset dir="${dest}/net/java/sip/communicator/impl/media/codec/video"
prefix="net/java/sip/communicator/impl/media/codec/video"/>
<zipfileset dir="${dest}/net/java/sip/communicator/impl/media/conference"
prefix="net/java/sip/communicator/impl/media/conference"/>
<zipfileset dir="${dest}/net/java/sip/communicator/impl/media/protocol"
prefix="net/java/sip/communicator/impl/media/protocol"/>
<zipfileset dir="${dest}/net/java/sip/communicator/impl/media/renderer"
@ -1650,6 +1656,16 @@ javax.swing.event, javax.swing.border"/>
</jar>
</target>
<!--BUNDLE-GALAGONOTIFICATION-->
<target name="bundle-galagonotification">
<!-- Creates a bundle for the galagonotification plugin. -->
<jar compress="false" destfile="${bundles.dest.lin}/galagonotification.jar"
manifest="${src}/net/java/sip/communicator/impl/galagonotification/galagonotification.manifest.mf">
<zipfileset dir="${dest}/net/java/sip/communicator/impl/galagonotification"
prefix="net/java/sip/communicator/impl/galagonotification" />
</jar>
</target>
<!--BUNDLE-POPUPMESSAGEHANDLER-SLICK-->
<target name="bundle-popupmessagehandler-slick">
<jar compress="false" destfile="${bundles.dest}/popupmessagehandler-slick.jar"

@ -402,6 +402,7 @@ impl.systray.FAILED_TO_OPEN_ADD_CONTACT_DIALOG=Failed to open the "Add Contact"
impl.systray.POPUP_MESSAGE_HANDLER=Systray balloon messages
impl.swingnotification.POPUP_MESSAGE_HANDLER=SIP Communicator popups
impl.growlnotification.POPUP_MESSAGE_HANDLER=Growl notifications
impl.galagonotification.POPUP_MESSAGE_HANDLER=freedesktop.org Desktop Notifications
# New Account Dialog
impl.gui.main.account.DEFAULT_PAGE_TITLE=Select Network

@ -0,0 +1,15 @@
JAVA_HOME?=/usr/lib/jvm/java-6-sun
ARCH=$(shell uname -m | sed -e s/x86_64/-64/ -e s/i.86//)
TARGET=../../../../lib/native/linux$(ARCH)/libgalagonotification.so
CC=gcc
CPPFLAGS=-DJNI_IMPLEMENTATION \
-Wall -Wreturn-type \
-I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux \
$(shell pkg-config --cflags dbus-1)
LDFLAGS=-shared
LIBS=$(shell pkg-config --libs dbus-1)
$(TARGET): net_java_sip_communicator_impl_galagonotification_GalagoNotification.c net_java_sip_communicator_impl_galagonotification_GalagoNotification.h
$(CC) $(CPPFLAGS) $< $(LDFLAGS) -o $@ $(LIBS)

@ -0,0 +1,292 @@
#include "net_java_sip_communicator_impl_galagonotification_GalagoNotification.h"
#include <dbus/dbus.h>
static dbus_bool_t
GalagoNotification_messageAppendString(
JNIEnv *env, DBusMessageIter *iter, jstring jstr)
{
const jbyte *str;
dbus_bool_t success;
const char *emptyStr = "";
if (jstr)
{
str = (*env)->GetStringUTFChars(env, jstr, NULL);
if (!str)
return FALSE;
}
else
str = NULL;
success
= dbus_message_iter_append_basic(
iter,
DBUS_TYPE_STRING,
str ? &str : &emptyStr);
if (str)
(*env)->ReleaseStringUTFChars(env, jstr, str);
return success;
}
static dbus_bool_t
GalagoNotification_notifyAppendArgs(
JNIEnv *env, DBusMessage *message, jstring appName, jlong replacesId,
jstring appIcon, jstring summary, jstring body)
{
DBusMessageIter iter;
dbus_uint32_t _replacesId;
DBusMessageIter subIter;
dbus_int32_t _expireTimeout;
dbus_message_iter_init_append(message, &iter);
if (!GalagoNotification_messageAppendString(env, &iter, appName))
return FALSE;
_replacesId = replacesId;
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &_replacesId))
return FALSE;
if (!GalagoNotification_messageAppendString(env, &iter, appIcon))
return FALSE;
if (!GalagoNotification_messageAppendString(env, &iter, summary))
return FALSE;
if (!GalagoNotification_messageAppendString(env, &iter, body))
return FALSE;
if (!dbus_message_iter_open_container(
&iter,
DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING_AS_STRING,
&subIter))
return FALSE;
if (!dbus_message_iter_close_container(&iter, &subIter))
return FALSE;
if (!dbus_message_iter_open_container(
&iter,
DBUS_TYPE_ARRAY,
"{sv}",
&subIter))
return FALSE;
if (!dbus_message_iter_close_container(&iter, &subIter))
return FALSE;
_expireTimeout = -1;
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &_expireTimeout))
return FALSE;
return TRUE;
}
static jobjectArray
GalagoNotification_stringArray2jstringArray(
JNIEnv *env, char **stringArray, int stringArraySize)
{
jclass stringClass;
jobjectArray jstringArray;
stringClass = (*env)->FindClass(env, "java/lang/String");
if (stringClass)
{
jstringArray
= (*env)->NewObjectArray(env, stringArraySize, stringClass, NULL);
if (jstringArray)
{
int i;
for (i = 0; i < stringArraySize; i++)
{
jstring jstr = (*env)->NewStringUTF(env, *(stringArray + i));
if (jstr)
{
(*env)->SetObjectArrayElement(env, jstringArray, i, jstr);
if ((*env)->ExceptionCheck(env))
{
jstringArray = NULL;
break;
}
}
else
{
jstringArray = NULL;
break;
}
}
}
}
else
jstringArray = NULL;
return jstringArray;
}
static void
GalagoNotification_throwException(JNIEnv *env, DBusError *error)
{
/* TODO Auto-generated method stub */
jclass clazz
= (*env)
->FindClass(
env,
"net/java/sip/communicator/impl/galagonotification/DBusException");
if (clazz)
(*env)->ThrowNew(env, clazz, error->message);
}
JNIEXPORT jlong JNICALL
Java_net_java_sip_communicator_impl_galagonotification_GalagoNotification_dbus_1bus_1get_1session(
JNIEnv *env, jclass clazz)
{
DBusError error;
DBusConnection *connection;
dbus_error_init(&error);
connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (connection)
dbus_connection_set_exit_on_disconnect(connection, FALSE);
else if (dbus_error_is_set(&error))
{
GalagoNotification_throwException(env, &error);
dbus_error_free(&error);
}
return connection;
}
JNIEXPORT void JNICALL
Java_net_java_sip_communicator_impl_galagonotification_GalagoNotification_dbus_1connection_1unref(
JNIEnv *env, jclass clazz, jlong connection)
{
dbus_connection_unref((DBusConnection *) connection);
}
JNIEXPORT jobjectArray JNICALL
Java_net_java_sip_communicator_impl_galagonotification_GalagoNotification_getCapabilities(
JNIEnv *env, jclass clazz, jlong connection)
{
DBusMessage *message;
jobjectArray jcapabilities = NULL;
message
= dbus_message_new_method_call(
"org.freedesktop.Notifications",
"/org/freedesktop/Notifications",
"org.freedesktop.Notifications",
"GetCapabilities");
if (message)
{
DBusError error;
DBusMessage *reply;
dbus_error_init(&error);
reply
= dbus_connection_send_with_reply_and_block(
(DBusConnection *) connection,
message,
-1,
&error);
if (reply)
{
char **capabilities;
int capabilityCount;
if (dbus_message_get_args(
reply,
&error,
DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING,
&capabilities,
&capabilityCount,
DBUS_TYPE_INVALID))
{
jcapabilities
= GalagoNotification_stringArray2jstringArray(
env,
capabilities,
capabilityCount);
dbus_free_string_array(capabilities);
}
else
{
GalagoNotification_throwException(env, &error);
dbus_error_free(&error);
}
dbus_message_unref(reply);
}
else if (dbus_error_is_set(&error))
{
GalagoNotification_throwException(env, &error);
dbus_error_free(&error);
}
dbus_message_unref(message);
}
return jcapabilities;
}
JNIEXPORT jlong JNICALL
Java_net_java_sip_communicator_impl_galagonotification_GalagoNotification_notify(
JNIEnv *env, jclass clazz, jlong connection, jstring appName,
jlong replacesId, jstring appIcon, jstring summary, jstring body)
{
DBusMessage *message;
jlong jid = 0;
message
= dbus_message_new_method_call(
"org.freedesktop.Notifications",
"/org/freedesktop/Notifications",
"org.freedesktop.Notifications",
"Notify");
if (message)
{
if (GalagoNotification_notifyAppendArgs(
env,
message,
appName,
replacesId,
appIcon,
summary,
body))
{
DBusError error;
DBusMessage *reply;
dbus_error_init(&error);
reply
= dbus_connection_send_with_reply_and_block(
(DBusConnection *) connection,
message,
-1,
&error);
if (reply)
{
dbus_uint32_t id;
if (dbus_message_get_args(
reply,
&error,
DBUS_TYPE_UINT32,
&id,
DBUS_TYPE_INVALID))
jid = id;
else
{
GalagoNotification_throwException(env, &error);
dbus_error_free(&error);
}
dbus_message_unref(reply);
}
else if (dbus_error_is_set(&error))
{
GalagoNotification_throwException(env, &error);
dbus_error_free(&error);
}
}
dbus_message_unref(message);
}
return jid;
}

@ -0,0 +1,45 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class net_java_sip_communicator_impl_galagonotification_GalagoNotification */
#ifndef _Included_net_java_sip_communicator_impl_galagonotification_GalagoNotification
#define _Included_net_java_sip_communicator_impl_galagonotification_GalagoNotification
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: net_java_sip_communicator_impl_galagonotification_GalagoNotification
* Method: dbus_bus_get_session
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_galagonotification_GalagoNotification_dbus_1bus_1get_1session
(JNIEnv *, jclass);
/*
* Class: net_java_sip_communicator_impl_galagonotification_GalagoNotification
* Method: dbus_connection_unref
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_galagonotification_GalagoNotification_dbus_1connection_1unref
(JNIEnv *, jclass, jlong);
/*
* Class: net_java_sip_communicator_impl_galagonotification_GalagoNotification
* Method: getCapabilities
* Signature: (J)[Ljava/lang/String;
*/
JNIEXPORT jobjectArray JNICALL Java_net_java_sip_communicator_impl_galagonotification_GalagoNotification_getCapabilities
(JNIEnv *, jclass, jlong);
/*
* Class: net_java_sip_communicator_impl_galagonotification_GalagoNotification
* Method: notify
* Signature: (JLjava/lang/String;JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)J
*/
JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_galagonotification_GalagoNotification_notify
(JNIEnv *, jclass, jlong, jstring, jlong, jstring, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,30 @@
/*
* 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.galagonotification;
/**
* Implements <tt>Exception</tt> for D-Bus errors reported through the native
* <tt>DBusError</tt> structure.
*
* @author Lubomir Marinov
*/
public class DBusException
extends Exception
{
/**
* Initializes a new <tt>DBusException</tt> instance with the specified
* detail message.
*
* @param message the detail message to later be reported by the new
* instance through its {@link #getMessage()}
*/
public DBusException(String message)
{
super(message);
}
}

@ -0,0 +1,95 @@
/*
* 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.galagonotification;
/**
* Declares the native functions required by the galagonotification bundle.
*
* @author Lubomir Marinov
*/
public final class GalagoNotification
{
static
{
System.loadLibrary("galagonotification");
}
/**
* Connects to the <tt>DBUS_BUS_SESSION</tt> D-Bus bus daemon and registers
* with it.
*
* @return a new reference to a <tt>DBusConnection</tt> to the
* <tt>DBUS_BUS_SESSION</tt> D-Bus bus daemon
* @throws DBusException if connecting to and registering with the
* <tt>DBUS_BUS_SESSION</tt> D-Bus bus daemon fails
*/
public static native long dbus_bus_get_session()
throws DBusException;
/**
* Decrements the reference count of the specified <tt>DBusConnection</tt>
* and finalizes it if the count reaches zero.
*
* @param connection the <tt>DBusConnection</tt> to decrement the reference
* count of
*/
public static native void dbus_connection_unref(long connection);
/**
* Invokes <tt>org.freedesktop.Notifications.GetCapabilities</tt> through
* the specified <tt>DBusConnection</tt> in order to retrieve the optional
* capabilities supported by the freedesktop.org Desktop Notifications
* server.
*
* @param connection the <tt>DBusConnection</tt> with the freedesktop.org
* Desktop Notifications server
* @return an array of <tt>String</tt>s listing the optional capabilities
* supported by the freedesktop.org Desktop Notifications server
* @throws DBusException if retrieving the optional capabilities of the
* freedesktop.org Desktop Notifications server fails
*/
public static native String[] getCapabilities(long connection)
throws DBusException;
/**
* Invokes <tt>org.freedesktop.Notifications.Notify</tt> through the
* specified <tt>DBusConnection</tt> in order to send a notification to the
* freedesktop.org Desktop Notifications server.
*
* @param connection the <tt>DBusConnection</tt> with the freedesktop.org
* Desktop Notifications server
* @param appName the optional name of the application sending the
* notification
* @param replacesId the optional notification identifier of an existing
* notification to be replaced by the notification being sent; <tt>0</tt> to
* not replace any existing notification
* @param appIcon the optional program icon of the application sending the
* notification. Not supported at this time.
* @param summary the summary text briefly describing the notification
* @param body the optional detailed body text of the notification
* @return the unique identifier of the sent notification if
* <tt>replacesId</tt> is <tt>0</tt>; <tt>replacesId</tt> if
* <tt>replacesId</tt> is not <tt>0</tt>
* @throws DBusException if sending the notification to the freedesktop.org
* Desktop Notifications server fails
*/
public static native long notify(
long connection,
String appName,
long replacesId,
String appIcon,
String summary,
String body)
throws DBusException;
/**
* Prevents the creation of <tt>GalagoNotification</tt> instances.
*/
private GalagoNotification()
{
}
}

@ -0,0 +1,159 @@
/*
* 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.galagonotification;
import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.service.systray.*;
import net.java.sip.communicator.util.*;
import org.osgi.framework.*;
/**
* Implements <tt>BundleActivator</tt> for the galagonotification bundle which
* provides an implementation of <tt>PopupMessageHandler</tt> according to the
* freedesktop.org Desktop Notifications spec.
*
* @author Lubomir Marinov
*/
public class GalagoNotificationActivator
implements BundleActivator
{
/**
* The <tt>Logger</tt> used by the <tt>GalagoNotificationActivator</tt>
* class and its instances for logging output.
*/
private static final Logger logger
= Logger.getLogger(GalagoNotificationActivator.class);
/**
* The context in which the galagonotification bundle is executing.
*/
private static BundleContext bundleContext;
/**
* The <tt>DBusConnection</tt> pointer to the D-Bus connection through which
* the freedesktop.org Desktop Notifications are being sent.
*/
static long dbusConnection;
/**
* The resources such as internationalized and localized text and images
* used by the galagonotification bundle.
*/
private static ResourceManagementService resources;
/**
* Gets the resources such as internationalized and localized text and
* images used by the galagonotification bundle.
*
* @return the resources such as internationalized and localized text and
* images used by the galagonotification bundle
*/
public static ResourceManagementService getResources()
{
if (resources == null)
resources = ResourceManagementServiceUtils.getService(bundleContext);
return resources;
}
/**
* Starts the galagonotification bundle. Registers its
* <tt>PopupMessageHandler</tt> implementation if it is supported by the
* current operating system.
*
* @param bundleContext the context in which the galagonotification bundle
* is to execute
* @throws Exception if the <tt>PopupMessageHandler</tt> implementation of
* the galagonotification bundle is not supported by the current operating
* system
* @see BundleActivator#start(BundleContext)
*/
public void start(BundleContext bundleContext)
throws Exception
{
long dbusConnection = GalagoNotification.dbus_bus_get_session();
if (dbusConnection != 0)
{
/*
* We don't much care about the very capabilities because they are
* optional, we just want to make sure that the service exists.
*/
String[] capabilities
= GalagoNotification.getCapabilities(dbusConnection);
if (capabilities != null)
{
if (logger.isDebugEnabled())
{
logger
.debug(
"org.freedesktop.Notifications.GetCapabilities:");
for (String capability : capabilities)
logger.debug("\t" + capability);
}
/*
* The native counterpart may return null without throwing an
* exception even when it fails to retrieve the capabilities. So
* it will not be safe to use galagonotification in this case.
* It is also unclear whether the server will return null when
* it does not support any optional capabilities. So it's not
* totally safe to assume that null means that
* galagonotification cannot be used. Anyway, displaying only
* the message title may be insufficient for our purposes so
* we'll require the optional "body" capability and solve the
* above questions.
*/
boolean bodyIsImplemented = false;
for (String capability : capabilities)
if ("body".equals(capability))
{
bodyIsImplemented = true;
break;
}
if (bodyIsImplemented)
{
GalagoNotificationActivator.bundleContext = bundleContext;
GalagoNotificationActivator.dbusConnection = dbusConnection;
bundleContext
.registerService(
PopupMessageHandler.class.getName(),
new GalagoPopupMessageHandler(),
null);
}
else
GalagoNotification.dbus_connection_unref(dbusConnection);
}
}
}
/**
* Stops the galagonotification bundle.
*
* @param bundleContext the context in which the galagonotification bundle
* is to stop its execution
* @throws Exception if there was an error during the stopping of the native
* functionality supporting the <tt>PopupNotificationHandler</tt>
* implementation of the galagonotification bundle
*/
public void stop(BundleContext bundleContext)
throws Exception
{
if (dbusConnection != 0)
{
GalagoNotification.dbus_connection_unref(dbusConnection);
dbusConnection = 0;
}
if (bundleContext.equals(GalagoNotificationActivator.bundleContext))
GalagoNotificationActivator.bundleContext = null;
}
}

@ -0,0 +1,85 @@
/*
* 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.galagonotification;
import net.java.sip.communicator.service.systray.*;
import net.java.sip.communicator.util.*;
/**
* Implements <tt>PopupMessageHandler</tt> according to the freedesktop.org
* Desktop Notifications spec.
*
* @author Lubomir Marinov
*/
public class GalagoPopupMessageHandler
extends AbstractPopupMessageHandler
{
/**
* The <tt>Logger</tt> used by the <tt>GalagoPopupMessageHandler</tt> class
* and its instances for logging output.
*/
private static final Logger logger
= Logger.getLogger(GalagoPopupMessageHandler.class);
/**
* Returns the preference index of this <tt>PopupMessageHandler</tt> which
* indicates how many features it supports.
*
* @return the preference index of this <tt>PopupMessageHandler</tt> which
* indicates how many features it supports
* @see PopupMessageHandler#getPreferenceIndex()
*/
public int getPreferenceIndex()
{
return 1; // using a native popup mechanism
}
/**
* Shows the title and the message of the specified <tt>PopupMessage</tt>.
*
* @param popupMessage the <tt>PopupMessage</tt> specifying the title and
* the message to be shown
* @see PopupMessageHandler#showPopupMessage(PopupMessage)
*/
public void showPopupMessage(PopupMessage popupMessage)
{
try
{
GalagoNotification
.notify(
GalagoNotificationActivator.dbusConnection,
null,
0,
null,
popupMessage.getMessageTitle(),
popupMessage.getMessage());
}
catch (DBusException dbe)
{
logger.error("Failed to show PopupMessage " + popupMessage, dbe);
}
}
/**
* Gets the human-readable localized description of this
* <tt>PopupMessageHandler</tt>.
*
* @return the human-readable localized description of this
* <tt>PopupMessageHandler</tt>
* @see PopupMessageHandler#toString()
*/
@Override
public String toString()
{
return
GalagoNotificationActivator
.getResources()
.getI18NString(
"impl.galagonotification.POPUP_MESSAGE_HANDLER");
}
}

@ -0,0 +1,10 @@
Bundle-Activator: net.java.sip.communicator.impl.galagonotification.GalagoNotificationActivator
Bundle-Name: Desktop Notifications Provider
Bundle-Description: A bundle which implements notifications according to the freedesktop.org Desktop Notifications spec.
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
System-Bundle: yes
Import-Package: net.java.sip.communicator.service.systray,
net.java.sip.communicator.service.resources,
net.java.sip.communicator.util,
org.osgi.framework
Loading…
Cancel
Save