From e991bdb154cb4a5ec38f8905a7773c45d06a1880 Mon Sep 17 00:00:00 2001 From: Lyubomir Marinov Date: Sun, 25 Oct 2009 12:58:30 +0000 Subject: [PATCH] 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. --- build.xml | 20 +- lib/native/linux/libgalagonotification.so | Bin 0 -> 13209 bytes resources/languages/resources.properties | 1 + src/native/linux/galagonotification/Makefile | 15 + ...pl_galagonotification_GalagoNotification.c | 292 ++++++++++++++++++ ...pl_galagonotification_GalagoNotification.h | 45 +++ .../galagonotification/DBusException.java | 30 ++ .../GalagoNotification.java | 95 ++++++ .../GalagoNotificationActivator.java | 159 ++++++++++ .../GalagoPopupMessageHandler.java | 85 +++++ .../galagonotification.manifest.mf | 10 + 11 files changed, 750 insertions(+), 2 deletions(-) create mode 100755 lib/native/linux/libgalagonotification.so create mode 100644 src/native/linux/galagonotification/Makefile create mode 100644 src/native/linux/galagonotification/net_java_sip_communicator_impl_galagonotification_GalagoNotification.c create mode 100644 src/native/linux/galagonotification/net_java_sip_communicator_impl_galagonotification_GalagoNotification.h create mode 100644 src/net/java/sip/communicator/impl/galagonotification/DBusException.java create mode 100644 src/net/java/sip/communicator/impl/galagonotification/GalagoNotification.java create mode 100644 src/net/java/sip/communicator/impl/galagonotification/GalagoNotificationActivator.java create mode 100644 src/net/java/sip/communicator/impl/galagonotification/GalagoPopupMessageHandler.java create mode 100644 src/net/java/sip/communicator/impl/galagonotification/galagonotification.manifest.mf diff --git a/build.xml b/build.xml index b9a1ec25b..94bd3eb55 100644 --- a/build.xml +++ b/build.xml @@ -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"/> + + + + + + + + + + + )kAi7U>-3LK)sfmd!%RP1Os0YpZLG2D?{_~oFE621 zCx7@yZsz9PbIS%RXz=#ner&|2VeA3Ad`}j?}ps3 zKMLJ4qXmlMEy#;?YYa^^=sH$i!hbql4tl9BzZrBM@&e>WWR7tz@`X|a0`}!k9Q#Gc zi;&-h{5ItI$S{dmh%D^`3e(CY?V-zW1-%TJdO1e+PkFUvRpiiP8|SdjVDA6wZ=V0j z^I!Z#oAtZl%|ln+cJ-Ctxc>AVdl&z>a(>>QfAg1l_dWC^yPJ@n_YIad@v)*A{4yV! z{N+9X@n7-D$=d<>AP1}y{9EGDvpHGTbrxN_+i~4Q+Oabb*omGVCzrB26K*Csg&2B0jhRB4M_W6Y z&v}VV&M8a*NM`e{<3nsu97xzX$Fpx^!OiqQ*4TCaT*28tX{f4p`uc@A+7dm9&P+DrWr8~+=iG^1;&tUy zb~2I8P8y8MEpuna>#_?@Pxhdl;10^>lea6R*4r;uh2~EyDwV$NgnIL`b*cp+fqSrhmZ$hy1hf8Klo^I;81e zHN62HP`*Xeotk!N+OSH=zl{FE(7g@)kv@n_`VZEluMl}WjP_}IH7OoQeVVRCe_=ep z<^x0e1L!B+%6iN!NeW#(qp@@D^u3gWFKYTD;^5<6Aw?fI zvmfMZh{r^qArE>ddCX-l+kt+QJjzd!;vqYWIMA(>gC5ef8pm@JzI2!rYjKX050F)O zHlsdD3cjQN#rD#0vo$0nW1;L&f2Nxw89c`QAmqW10)g_l~O4+)9ac=)d-K z>Cx!ETL;RETLzAyZE1MzpZJ9@I%G-y@PuNX=XT4OB8YOkSUjrCi@L$fQ^2x!?FM(YMBi@FliNGs4T{nn^z zu~c_@0-d6U@IaqsYqa00maXa&obTb`6H?QH{-eFnQ??fMAN>Rh73-uLXuS)i1|!k} z&Y(IQPz-z#pkMie7kjpRa?Y=09CfbK|%nhi_RBH-fEhEPXUu3 zx=5p-awDeZF?BFJt5zqiR6ht zS+mMWa2W(H64)@y7Z1aMCA?xUQ6juFpgn+TZZcQ0)WwR%m>~bouQ`2WFitTh0B}quQYlTEkpjPVn<@ z)Wny<#AWNhRkZz)&|gBbYFc9YruR3@2OHK82OeDIj^QT;jv=Y{g+BPe!vid?6jEcEEHGn%84JuEUZP zo>{Yg-AW4LNw*kJ7Q9TXU0kRgAY$vzj^@_(W?VvTh^-c(Lp#t@Snh&o=sPGkBgegN ze3K`OySEiGUd;Wk^o8Ccnr3&*jx=1}u&m({?$XIM-$rd%i4(ABt=-;wj>lmNunF;5KHIL5dUIQn`((GQ8dEMypw}4<#ZN#WsBd2a8az;Om2VP zloOqu1?Pa_vWNvRW!1&CT!94J_>rpgN)-!KHrf+7M z{+YQMWq-e5#RrkGZDbqzd1lVV=p&x~{$?MJ&jn_Vn>apG3@(W>$I9mo_bl@&luaUz z>7&R7$9>Bj#1WhE;TZG9EN?MU&%`k>NiPA)etAZidHraT>6bsNvR~ST=Y;to0|GU; zK9mjaemwk`jf`+p{d_PtG>fe;nEL%e&_)aPk@n^-RPupFlP^z7LE5$9Dmf4DS1=FgSU) z-#k7hVu@q=Au{{t_!yVe*gW-odA6v}B;p=145*Pk<||WtvKg^WEa+rx$@6O3P}H;( zdqHDJjO>iB%=**7wXmR*!MzOLlqnhm?lp}eF|t>EWhuT&h>fzK{+aP%yGffE(MI4J zj1p9uA}RN%N!wOnE^tg(7O@VQcIMv4ahRZQFYeM?>A-X{`V8-D{6;BVU&a~py z4mQuZhvDcr*VH-tsqsr*CBndbp z+l&3*1C~CNDTGK?`Y(U~-v)n7EraYoI6wLclQ`c90E@9r=qothcY=QkJl@bGzZ3j# zF{nA9l-~vVM|>o0*Z2p(oAnHZ&(|ITZ`LyuUMPM+-i#jsKu>~yo7$EljDR1;tD5f= zwjT!XkGI_7JG`9sEk3>({Otk$Zt#3&F$DaD@Hw zqu)?RTK6Z!uLJz=!TaMqmt*10{${-CTI~YVcoCx%;O_|V#{&F=;QjHdGJHvnMty4z z1n@6__xC>(kT>{z9__Wn$6pEldho{ITuiU&d*#P>fa?|a@5kG}ZMW^YX}7)O#`Zl7 zYPB~{;I_BkOsbdMdxU%wwo}FK z?t`WuYv*R!&6eG*AFyo0u^Uq!??t{r%U-4BBWJ^O}5I2Tzv^U1xy7tDpV_@ zE2qHVBLiWUt&Fm~<~AQQ71rx2?uEZDd9G6G!k6Ep^b;dOlR1l*Ky)sy3* QCkFCw+~^NUR#EN$1`*e*JOBUy literal 0 HcmV?d00001 diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index 98febac07..36f257717 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -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 diff --git a/src/native/linux/galagonotification/Makefile b/src/native/linux/galagonotification/Makefile new file mode 100644 index 000000000..6311be99f --- /dev/null +++ b/src/native/linux/galagonotification/Makefile @@ -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) diff --git a/src/native/linux/galagonotification/net_java_sip_communicator_impl_galagonotification_GalagoNotification.c b/src/native/linux/galagonotification/net_java_sip_communicator_impl_galagonotification_GalagoNotification.c new file mode 100644 index 000000000..6c4b98358 --- /dev/null +++ b/src/native/linux/galagonotification/net_java_sip_communicator_impl_galagonotification_GalagoNotification.c @@ -0,0 +1,292 @@ +#include "net_java_sip_communicator_impl_galagonotification_GalagoNotification.h" + +#include + +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; +} diff --git a/src/native/linux/galagonotification/net_java_sip_communicator_impl_galagonotification_GalagoNotification.h b/src/native/linux/galagonotification/net_java_sip_communicator_impl_galagonotification_GalagoNotification.h new file mode 100644 index 000000000..2f3882abc --- /dev/null +++ b/src/native/linux/galagonotification/net_java_sip_communicator_impl_galagonotification_GalagoNotification.h @@ -0,0 +1,45 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* 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 diff --git a/src/net/java/sip/communicator/impl/galagonotification/DBusException.java b/src/net/java/sip/communicator/impl/galagonotification/DBusException.java new file mode 100644 index 000000000..fca706b3d --- /dev/null +++ b/src/net/java/sip/communicator/impl/galagonotification/DBusException.java @@ -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 Exception for D-Bus errors reported through the native + * DBusError structure. + * + * @author Lubomir Marinov + */ +public class DBusException + extends Exception +{ + + /** + * Initializes a new DBusException 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); + } +} diff --git a/src/net/java/sip/communicator/impl/galagonotification/GalagoNotification.java b/src/net/java/sip/communicator/impl/galagonotification/GalagoNotification.java new file mode 100644 index 000000000..5a1dd2e43 --- /dev/null +++ b/src/net/java/sip/communicator/impl/galagonotification/GalagoNotification.java @@ -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 DBUS_BUS_SESSION D-Bus bus daemon and registers + * with it. + * + * @return a new reference to a DBusConnection to the + * DBUS_BUS_SESSION D-Bus bus daemon + * @throws DBusException if connecting to and registering with the + * DBUS_BUS_SESSION D-Bus bus daemon fails + */ + public static native long dbus_bus_get_session() + throws DBusException; + + /** + * Decrements the reference count of the specified DBusConnection + * and finalizes it if the count reaches zero. + * + * @param connection the DBusConnection to decrement the reference + * count of + */ + public static native void dbus_connection_unref(long connection); + + /** + * Invokes org.freedesktop.Notifications.GetCapabilities through + * the specified DBusConnection in order to retrieve the optional + * capabilities supported by the freedesktop.org Desktop Notifications + * server. + * + * @param connection the DBusConnection with the freedesktop.org + * Desktop Notifications server + * @return an array of Strings 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 org.freedesktop.Notifications.Notify through the + * specified DBusConnection in order to send a notification to the + * freedesktop.org Desktop Notifications server. + * + * @param connection the DBusConnection 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; 0 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 + * replacesId is 0; replacesId if + * replacesId is not 0 + * @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 GalagoNotification instances. + */ + private GalagoNotification() + { + } +} diff --git a/src/net/java/sip/communicator/impl/galagonotification/GalagoNotificationActivator.java b/src/net/java/sip/communicator/impl/galagonotification/GalagoNotificationActivator.java new file mode 100644 index 000000000..c8254ab88 --- /dev/null +++ b/src/net/java/sip/communicator/impl/galagonotification/GalagoNotificationActivator.java @@ -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 BundleActivator for the galagonotification bundle which + * provides an implementation of PopupMessageHandler according to the + * freedesktop.org Desktop Notifications spec. + * + * @author Lubomir Marinov + */ +public class GalagoNotificationActivator + implements BundleActivator +{ + + /** + * The Logger used by the GalagoNotificationActivator + * 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 DBusConnection 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 + * PopupMessageHandler 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 PopupMessageHandler 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 PopupNotificationHandler + * 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; + } +} diff --git a/src/net/java/sip/communicator/impl/galagonotification/GalagoPopupMessageHandler.java b/src/net/java/sip/communicator/impl/galagonotification/GalagoPopupMessageHandler.java new file mode 100644 index 000000000..4ddb5d352 --- /dev/null +++ b/src/net/java/sip/communicator/impl/galagonotification/GalagoPopupMessageHandler.java @@ -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 PopupMessageHandler according to the freedesktop.org + * Desktop Notifications spec. + * + * @author Lubomir Marinov + */ +public class GalagoPopupMessageHandler + extends AbstractPopupMessageHandler +{ + + /** + * The Logger used by the GalagoPopupMessageHandler class + * and its instances for logging output. + */ + private static final Logger logger + = Logger.getLogger(GalagoPopupMessageHandler.class); + + /** + * Returns the preference index of this PopupMessageHandler which + * indicates how many features it supports. + * + * @return the preference index of this PopupMessageHandler 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 PopupMessage. + * + * @param popupMessage the PopupMessage 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 + * PopupMessageHandler. + * + * @return the human-readable localized description of this + * PopupMessageHandler + * @see PopupMessageHandler#toString() + */ + @Override + public String toString() + { + return + GalagoNotificationActivator + .getResources() + .getI18NString( + "impl.galagonotification.POPUP_MESSAGE_HANDLER"); + } +} diff --git a/src/net/java/sip/communicator/impl/galagonotification/galagonotification.manifest.mf b/src/net/java/sip/communicator/impl/galagonotification/galagonotification.manifest.mf new file mode 100644 index 000000000..adeaa3ab7 --- /dev/null +++ b/src/net/java/sip/communicator/impl/galagonotification/galagonotification.manifest.mf @@ -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