/*
* Copyright 2007, 2008, 2009 Duncan McGregor
*
* This file is part of Rococoa, a library to allow Java to talk to Cocoa.
*
* Rococoa is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rococoa is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Rococoa. If not, see <http://www.gnu.org/licenses/>.
*/
package org.rococoa.contrib.growl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.rococoa.cocoa.NSApplication;
import org.rococoa.cocoa.NSDistributedNotificationCenter;
import org.rococoa.cocoa.foundation.NSArray;
import org.rococoa.cocoa.foundation.NSDictionary;
import org.rococoa.cocoa.foundation.NSImage;
import org.rococoa.cocoa.foundation.NSMutableDictionary;
import org.rococoa.cocoa.foundation.NSNumber;
import org.rococoa.cocoa.foundation.NSObject;
import org.rococoa.cocoa.foundation.NSString;
/**
* A class that encapsulates the work of talking to Growl.
*
* @author Karl Adam (original code using CocoaJavaBridge)
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> (port to Rococoa).
* @author last modified by $Author: haraldk$
* @version $Id: Growl.java,v 1.0 Mar 26, 2009 12:24:28 PM haraldk Exp$
*/
public final class Growl {
// TODO: Consider allowing Icon/(Buffered)Image to be passed instead of NSImage
// TODO: Consider using Map instead of NSDictionary
// defines
/** The name of the growl registration notification for DNC. */
public static final String GROWL_APP_REGISTRATION = "GrowlApplicationRegistrationNotification";
// Ticket Defines
/** Ticket key for the application name. */
public static final String GROWL_APP_NAME = "ApplicationName";
/** Ticket key for the application icon. */
public static final String GROWL_APP_ICON = "ApplicationIcon";
/** Ticket key for the default notifactions. */
public static final String GROWL_NOTIFICATIONS_DEFAULT = "DefaultNotifications";
/** Ticket key for all notifactions. */
public static final String GROWL_NOTIFICATIONS_ALL = "AllNotifications";
// Notification Defines
/** The name of the growl notification for DNC. */
public static final String GROWL_NOTIFICATION = "GrowlNotification";
/** Notification key for the name. */
public static final String GROWL_NOTIFICATION_NAME = "NotificationName";
/** Notification key for the title. */
public static final String GROWL_NOTIFICATION_TITLE = "NotificationTitle";
/** Notification key for the description. */
public static final String GROWL_NOTIFICATION_DESCRIPTION = "NotificationDescription";
/** Notification key for the icon. */
public static final String GROWL_NOTIFICATION_ICON = "NotificationIcon";
/** Notification key for the application icon. */
public static final String GROWL_NOTIFICATION_APP_ICON = "NotificationAppIcon";
/** Notification key for the sticky flag. */
public static final String GROWL_NOTIFICATION_STICKY = "NotificationSticky";
/** Notification key for the identifier. */
public static final String GROWL_NOTIFICATION_IDENTIFIER = "GrowlNotificationIdentifier";
// Actual instance data
// We should only register once
private boolean registered;
// "Application" Name
private String appName;
// "application" Icon
private NSImage appImage;
// All notifications
private List<String> allNotes;
// Default enabled notifications
private List<String> defNotes;
// The notification center
private final NSDistributedNotificationCenter theCenter;
private static NSArray toNSArray(final List<String> strings) {
if (strings == null) {
return null;
}
return toNSArray(strings.toArray(new String[strings.size()]));
}
private static NSArray toNSArray(final String... strings) {
if (strings == null) {
return null;
}
NSObject[] types = new NSObject[strings.length];
for (int i = 0; i < strings.length; i++) {
types[i] = NSString.stringWithString(strings[i]);
}
return NSArray.CLASS.arrayWithObjects(types);
}
// private static NSDictionary toNSDictionary(final Map<?, ?> map) {
// NSDictionary dictionary = NSDictionary.dictionaryWithObjects_forKeys(objects, keys);
// return dictionary;
// }
//
// private static NSImage toNSImage(final Image image) {
// return null;
// }
//************ Constructors **************//
/**
* Convenience method to contruct a growl instance, defers to Growl(String
* inAppName, NSData inImageData, NSArray inAllNotes, NSArray inDefNotes,
* boolean registerNow) with empty arrays for your notifications.
*
* @param inAppName The Name of your "application"
* @param inImage The NSImage Icon for your Application
*/
public Growl(String inAppName, NSImage inImage) {
this(inAppName, inImage, null, null, false);
}
/**
* Convenience method to contruct a growl instance, defers to Growl(String
* inAppName, NSData inImageData, NSArray inAllNotes, NSArray inDefNotes,
* boolean registerNow) with the arrays passed here and empty Data for the icon.
*
* @param inAppName The Name of your "Application"
* @param inAllNotes A String Array with the name of all your notifications
* @param inDefNotes A String Array with the na,es of the Notifications on
* by default
*/
public Growl(String inAppName, List<String> inAllNotes, List<String> inDefNotes) {
this(inAppName, null, inAllNotes, inDefNotes, false);
}
/**
* Convenience method to contruct a growl instance, defers to Growl(String
* inAppName, NSData inImageData, NSArray inAllNotes, NSArray inDefNotes,
* boolean registerNow) with empty arrays for your notifications.
*
* @param inAppName The Name of your "Application"
* @param inImage Your "Application"'s icon, or {@code null} to use your application's default application icon.
* @param inAllNotes The NSArray of Strings of all your Notifications
* @param inDefNotes The NSArray of Strings of your default Notifications
* @param registerNow Since we have all the necessary info we can go ahead
* and register
*/
public Growl(String inAppName, NSImage inImage, List<String> inAllNotes, List<String> inDefNotes, boolean registerNow) {
if (inAppName == null) {
throw new IllegalArgumentException("Application name may not be null");
}
appName = inAppName;
appImage = inImage != null ? inImage : NSApplication.NSApp.applicationIconImage();
setAllowedNotifications(inAllNotes);
setDefaultNotifications(inDefNotes);
theCenter = NSDistributedNotificationCenter.defaultCenter();
if (registerNow) {
register();
}
}
//************ Commonly Used Methods **************//
// TODO: What's the point of this return value? It's always true...
/**
* Register all our notifications with Growl, this should only be called once.
*
* @return <code>true</code>.
*/
public final boolean register() {
if (!registered) {
// Construct our dictionary
// Make the arrays of objects then keys
NSArray objects = NSArray.CLASS.arrayWithObjects(
NSString.stringWithString(appName),
toNSArray(allNotes),
toNSArray(defNotes),
appImage != null ? appImage.TIFFRepresentation() : null
);
NSArray keys = NSArray.CLASS.arrayWithObjects(
NSString.stringWithString(GROWL_APP_NAME),
NSString.stringWithString(GROWL_NOTIFICATIONS_ALL),
NSString.stringWithString(GROWL_NOTIFICATIONS_DEFAULT),
appImage != null ? NSString.stringWithString(GROWL_APP_ICON) : null
);
// Make the Dictionary
NSDictionary regDict = NSDictionary.dictionaryWithObjects_forKeys(objects, keys);
theCenter.postNotification(
GROWL_APP_REGISTRATION, // notificationName
null, // anObject
regDict, // userInfoDictionary
true // deliverImmediately
);
registered = true;
}
return true;
}
/**
* The fun part is actually sending those notifications we worked so hard for
* so here we let growl know about things we think the user would like, and growl
* decides if that is the case.
*
* @param inNotificationName The name of one of the notifications we told growl
* about.
* @param inIcon The NSImage for the icon for this notification, can be null
* @param inTitle The Title of our Notification as Growl will show it
* @param inDescription The Description of our Notification as Growl will
* display it
* @param inExtraInfo Growl is flexible and allows Display Plugins to do as they
* please with thier own special keys and values, you may use
* them here. These may be ignored by either the user's
* preferences or the current Display Plugin. This can be null
* @param inSticky Whether the Growl notification should be sticky
* @param inIdentifier Notification identifier for coalescing. This can be null.
* @throws IllegalArgumentException When a notification is not known
*/
public void postNotification(String inNotificationName, NSImage inIcon, String inTitle, String inDescription,
NSDictionary inExtraInfo, boolean inSticky, String inIdentifier) {
NSMutableDictionary noteDict = NSMutableDictionary.dictionaryWithCapacity(0);
if (!allNotes.contains(inNotificationName)) {
throw new IllegalArgumentException("Undefined Notification attempted: " + inNotificationName);
}
noteDict.setValue_forKey(NSString.stringWithString(inNotificationName), GROWL_NOTIFICATION_NAME);
noteDict.setValue_forKey(inTitle != null ? NSString.stringWithString(inTitle) : null, GROWL_NOTIFICATION_TITLE);
noteDict.setValue_forKey(inDescription != null ? NSString.stringWithString(inDescription) : null, GROWL_NOTIFICATION_DESCRIPTION);
noteDict.setValue_forKey(NSString.stringWithString(appName), GROWL_APP_NAME);
if (inIcon != null) {
noteDict.setValue_forKey(inIcon.TIFFRepresentation(), GROWL_NOTIFICATION_ICON);
}
if (inSticky) {
noteDict.setValue_forKey(NSNumber.CLASS.numberWithInt(1), GROWL_NOTIFICATION_STICKY);
}
if (inIdentifier != null) {
noteDict.setValue_forKey(NSString.stringWithString(inIdentifier), GROWL_NOTIFICATION_IDENTIFIER);
}
if (inExtraInfo != null) {
noteDict.addEntriesFromDictionary(inExtraInfo);
}
theCenter.postNotification(GROWL_NOTIFICATION, null, noteDict, true);
}
/**
* Convenience method that defers to postNotificationGrowlOf(String inNotificationName,
* NSData inIconData, String inTitle, String inDescription,
* NSDictionary inExtraInfo, boolean inSticky, String inIdentifier).
* This is primarily for compatibility with older code
*
* @param inNotificationName The name of one of the notifications we told growl
* about.
* @param inIcon The NSData for the icon for this notification, can be null
* @param inTitle The Title of our Notification as Growl will show it
* @param inDescription The Description of our Notification as Growl will
* display it
* @param inExtraInfo Growl is flexible and allows Display Plugins to do as
* they please with their own special keys and values, you
* may use them here. These may be ignored by either the
* user's preferences or the current Display Plugin. This
* can be null.
* @param inSticky Whether the Growl notification should be sticky.
* @throws IllegalArgumentException When a notification is not known
*/
public void postNotification(String inNotificationName, NSImage inIcon, String inTitle, String inDescription,
NSDictionary inExtraInfo, boolean inSticky) {
postNotification(inNotificationName, inIcon, inTitle, inDescription, inExtraInfo, inSticky, null);
}
/**
* Convenience method that defers to postNotificationGrowlOf(String inNotificationName,
* NSData inIconData, String inTitle, String inDescription,
* NSDictionary inExtraInfo, boolean inSticky, String inIdentifier).
* This is primarily for compatibility with older code
*
* @param inNotificationName The name of one of the notifications we told growl
* about.
* @param inIcon The NSData for the icon for this notification, can be null
* @param inTitle The Title of our Notification as Growl will show it
* @param inDescription The Description of our Notification as Growl will
* display it
* @param inExtraInfo Growl is flexible and allows Display Plugins to do as
* they please with their own special keys and values, you
* may use them here. These may be ignored by either the
* user's preferences or the current Display Plugin. This
* can be null.
* @throws IllegalArgumentException When a notification is not known
*/
public void postNotification(String inNotificationName, NSImage inIcon, String inTitle, String inDescription,
NSDictionary inExtraInfo) {
postNotification(inNotificationName, inIcon, inTitle, inDescription, inExtraInfo, false, null);
}
/**
* Convenienve method that defers to postNotificationGrowlOf(String inNotificationName,
* NSData inIconData, String inTitle, String inDescription,
* NSDictionary inExtraInfo, boolean inSticky, String inIdentifier) with
* <code>null</code> passed for icon, extraInfo and identifier arguments
*
* @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 IllegalArgumentException When a notification is not known
*/
public void postNotification(String inNotificationName, String inTitle, String inDescription) {
postNotification(inNotificationName, null, inTitle, inDescription, null, false, null);
}
/**
* Convenience method that defers to postNotificationGrowlOf(String inNotificationName,
* NSData inIconData, String inTitle, String inDescription,
* NSDictionary inExtraInfo, boolean inSticky)
* with <code>null</code> passed for icon and extraInfo arguments.
*
* @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
* @param inSticky Whether our notification should be sticky
* @throws IllegalArgumentException When a notification is not known
*/
public void postNotification(String inNotificationName, String inTitle, String inDescription, boolean inSticky) {
postNotification(inNotificationName, null, inTitle, inDescription, null, inSticky, null);
}
//************ Accessors **************//
/**
* Accessor for The currently set "Application" Name
*
* @return String Application Name
*/
public String applicationName() {
return appName;
}
/**
* Accessor for the Array of allowed Notifications returned an NSArray
*
* @return the array of allowed notifications.
*/
public List<String> allowedNotifications() {
return allNotes;
}
/**
* Accessor for the Array of default Notifications returned as an NSArray
*
* @return the array of default notifications.
*/
public List<String> defaultNotifications() {
return defNotes;
}
//************ Mutators **************//
/**
* Sets The name of the Application talking to growl
*
* @param inAppName The Application Name
* @throws IllegalStateException if already registered
*/
public void setApplicationName(final String inAppName) {
if (registered) {
throw new IllegalStateException("Already registered");
}
appName = inAppName;
}
/**
* Set the list of allowed Notifications
*
* @param inAllNotes The array of allowed Notifications
* @throws IllegalStateException if already registered
*/
public void setAllowedNotifications(final List<String> inAllNotes) {
if (registered) {
throw new IllegalStateException("Already registered");
}
allNotes = Collections.unmodifiableList(new ArrayList<String>(inAllNotes));
}
/**
* Set the list of Default Notfiications
*
* @param inDefNotes The default Notifications
* @throws IllegalArgumentException when an element of the array is not in the
* allowedNotifications
* @throws IllegalStateException if already registered
*/
public void setDefaultNotifications(final List<String> inDefNotes) {
if (registered) {
throw new IllegalStateException("Already registered");
}
for (String inDefNote : inDefNotes) {
if (!allNotes.contains(inDefNote)) {
// TODO: This check is not done in the constructor
throw new IllegalArgumentException("Array Element not in Allowed Notifications");
}
}
defNotes = Collections.unmodifiableList(new ArrayList<String>(inDefNotes));
}
}