com.notifier.desktop.notification.broadcast.growl.GrowlNotificationBroadcaster.java Source code

Java tutorial

Introduction

Here is the source code for com.notifier.desktop.notification.broadcast.growl.GrowlNotificationBroadcaster.java

Source

/*
 * Android Notifier Desktop is a multiplatform remote notification client for Android devices.
 *
 * Copyright (C) 2010  Leandro Aparecido
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 3 of the License.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.notifier.desktop.notification.broadcast.growl;

import java.awt.image.*;
import java.io.*;
import java.net.*;
import java.util.*;

import javax.imageio.*;

import org.slf4j.*;

import com.binaryblizzard.growl.*;
import com.google.code.jgntp.*;
import com.google.common.base.*;
import com.google.common.collect.*;
import com.google.common.io.*;
import com.google.inject.*;
import com.notifier.desktop.*;
import com.notifier.desktop.notification.*;
import com.notifier.desktop.notification.broadcast.*;
import com.notifier.desktop.os.*;

import static java.util.concurrent.TimeUnit.*;

@Singleton
public class GrowlNotificationBroadcaster extends RestartableService implements NotificationBroadcaster {

    public static final String APPLICATION_ICON = "app-icon.png";

    private static final int GROWL_CONNECTION_ATTEMPTS = 10;
    private static final long NOTIFY_TIMEOUT = 1;
    private static final long SHUTDOWN_TIMEOUT = 10;

    private String REGISTRATION_ERROR_TITLE = Application.NAME + " Growl Registration Error";
    private String NOTIFICATION_ERROR_TITLE = Application.NAME + " Growl Notification Error";

    private static final Logger logger = LoggerFactory.getLogger(GrowlNotificationBroadcaster.class);

    private @Inject Application application;

    private GntpClient gntpClient;
    private EnumMap<Notification.Type, GntpNotificationInfo> notificationInfos;

    private GrowlPatched macGrowl;

    private int growlConnectionRetries;

    @Override
    public String getName() {
        return "growl";
    }

    @Override
    public void doStart() throws Exception {
        if (OperatingSystems.CURRENT_FAMILY == OperatingSystems.Family.MAC) {
            macGrowl = new GrowlPatched();
            macGrowl.addGrowlHost("localhost", null);
            GrowlRegistrations registrations = macGrowl.getRegistrations(Application.NAME);
            registrations.registerNotification(Notification.Type.RING.toString(), true);
            registrations.registerNotification(Notification.Type.SMS.toString(), true);
            registrations.registerNotification(Notification.Type.MMS.toString(), true);
            registrations.registerNotification(Notification.Type.BATTERY.toString(), true);
            registrations.registerNotification(Notification.Type.VOICEMAIL.toString(), true);
            registrations.registerNotification(Notification.Type.PING.toString(), true);
            registrations.registerNotification(Notification.Type.USER.toString(), true);
            macGrowl.sendRegistrations();
        } else {
            GntpApplicationInfo appInfo = Gntp.appInfo(Application.NAME).icon(getIcon(APPLICATION_ICON)).build();

            notificationInfos = Maps.newEnumMap(Notification.Type.class);
            notificationInfos.put(Notification.Type.RING, infoForType(appInfo, Notification.Type.RING));
            notificationInfos.put(Notification.Type.SMS, infoForType(appInfo, Notification.Type.SMS));
            notificationInfos.put(Notification.Type.MMS, infoForType(appInfo, Notification.Type.MMS));
            notificationInfos.put(Notification.Type.BATTERY, infoForType(appInfo, Notification.Type.BATTERY));
            notificationInfos.put(Notification.Type.VOICEMAIL, infoForType(appInfo, Notification.Type.VOICEMAIL));
            notificationInfos.put(Notification.Type.PING, infoForType(appInfo, Notification.Type.PING));
            notificationInfos.put(Notification.Type.USER, infoForType(appInfo, Notification.Type.USER));

            gntpClient = Gntp.client(appInfo).listener(new Listener()).build();
            gntpClient.register();
        }
    }

    @Override
    public void broadcast(Notification notification, String deviceName, boolean privateMode) {
        if (!isRunning()) {
            return;
        }
        if (OperatingSystems.CURRENT_FAMILY == OperatingSystems.Family.MAC) {
            GrowlNotification n = new GrowlNotification(notification.getType().toString(),
                    notification.getTitle(deviceName), notification.getDescription(privateMode), Application.NAME,
                    false, GrowlNotification.NORMAL);
            try {
                macGrowl.sendNotification(n);
            } catch (GrowlException e) {
                logger.error("Error sending notification using jgrowl", e);
                application.showError(NOTIFICATION_ERROR_TITLE, "Error sending notification to Growl.");
            }
        } else {
            try {
                if (notification.getType() == Notification.Type.BATTERY) {
                    String iconName = notification.getBatteryIconName();
                    try {
                        RenderedImage icon = getIcon(iconName);
                        gntpClient.notify(
                                Gntp.notification(notificationInfos.get(Notification.Type.BATTERY),
                                        notification.getTitle(deviceName))
                                        .text(notification.getDescription(privateMode)).icon(icon).build(),
                                NOTIFY_TIMEOUT, SECONDS);
                    } catch (IOException e) {
                        doNotify(notification, deviceName, privateMode);
                    }
                } else {
                    doNotify(notification, deviceName, privateMode);
                }
            } catch (InterruptedException e) {
                logger.debug("Interrupted sending GNTP notification");
                Thread.currentThread().interrupt();
            }
        }
    }

    @Override
    public void doStop() {
        if (OperatingSystems.CURRENT_FAMILY != OperatingSystems.Family.MAC) {
            try {
                gntpClient.shutdown(SHUTDOWN_TIMEOUT, SECONDS);
            } catch (InterruptedException e) {
                logger.debug("Interrupted shutting down GNTP client");
                Thread.currentThread().interrupt();
            }
        }
    }

    protected GntpNotificationInfo infoForType(GntpApplicationInfo appInfo, Notification.Type type)
            throws IOException {
        return Gntp.notificationInfo(appInfo, type.name()).displayName(type.getTitle())
                .icon(getIcon(type.getIconName())).build();
    }

    protected void doNotify(Notification notification, String deviceName, boolean privateMode)
            throws InterruptedException {
        GntpNotificationInfo info = notificationInfos.get(notification.getType());
        Preconditions.checkState(info != null, "Unknown notification type: %s", notification.getType());
        gntpClient.notify(Gntp.notification(info, notification.getTitle(deviceName))
                .text(notification.getDescription(privateMode)).build(), NOTIFY_TIMEOUT, SECONDS);
    }

    protected RenderedImage getIcon(String name) throws IOException {
        InputStream is = Notification.class.getResourceAsStream(name);
        Preconditions.checkState(is != null);
        try {
            return ImageIO.read(is);
        } finally {
            Closeables.closeQuietly(is);
        }
    }

    private class Listener implements GntpListener {

        private boolean notifiedGrowlNotRunning;
        private boolean notifiedGrowlAuthenticationError;
        private boolean notifiedInvalidRegistration;
        private boolean notifiedGrowlInternalError;
        private boolean notifiedNotRegistered;

        @Override
        public void onRegistrationSuccess() {
            logger.info("Registered with GNTP server sucessfully");
            notifiedGrowlAuthenticationError = false;
            notifiedGrowlInternalError = false;
            growlConnectionRetries = 0;
        }

        @Override
        public void onRegistrationError(GntpErrorStatus status, String description) {
            logger.error("GNTP server refused registration [{}-{}]", status, description);
            switch (status) {
            case NOT_AUTHORIZED:
                if (!notifiedGrowlAuthenticationError) {
                    notifiedGrowlAuthenticationError = true;
                    application.showError(REGISTRATION_ERROR_TITLE,
                            "Growl denied registration, did you set it to require password for LAN apps?");
                }
                break;
            case INVALID_REQUEST:
            case REQUIRED_HEADER_MISSING:
            case UNKNOWN_PROTOCOL:
            case UNKNOWN_PROTOCOL_VERSION:
                if (!notifiedInvalidRegistration) {
                    notifiedInvalidRegistration = true;
                    application.showError(REGISTRATION_ERROR_TITLE,
                            "Growl said I sent an invalid registration request, I may need updating.");
                }
                break;
            case INTERNAL_SERVER_ERROR:
            case NETWORK_FAILURE:
            case UNKNOWN_APPLICATION:
            case UNKNOWN_NOTIFICATION:
            case TIMED_OUT:
            case RESERVED:
                if (!notifiedGrowlInternalError) {
                    notifiedGrowlInternalError = true;
                    application.showError(REGISTRATION_ERROR_TITLE,
                            "Growl had trouble handling registration request. It may need restart or updating.");
                }
                break;
            }
        }

        @Override
        public void onNotificationSuccess(GntpNotification notification) {
            notifiedGrowlAuthenticationError = false;
            notifiedGrowlInternalError = false;
            notifiedNotRegistered = false;
        }

        @Override
        public void onClickCallback(GntpNotification notification) {
            // Do nothing
        }

        @Override
        public void onCloseCallback(GntpNotification notification) {
            // Do nothing
        }

        @Override
        public void onTimeoutCallback(GntpNotification notification) {
            // Do nothing
        }

        @Override
        public void onNotificationError(GntpNotification notification, GntpErrorStatus status, String description) {
            logger.error("GNTP server refused notification [{}-{}]", status, description);
            switch (status) {
            case NOT_AUTHORIZED:
                if (!notifiedGrowlAuthenticationError) {
                    notifiedGrowlAuthenticationError = true;
                    application.showError(NOTIFICATION_ERROR_TITLE,
                            "Growl denied notification, did you set it to require password for LAN apps?");
                }
                break;
            case UNKNOWN_APPLICATION:
            case UNKNOWN_NOTIFICATION:
                if (!notifiedNotRegistered) {
                    notifiedNotRegistered = true;
                    application.showError(NOTIFICATION_ERROR_TITLE,
                            "Growl said I am not registered, trying registration again (this notification has been lost).");
                }
                break;
            default:
                if (!notifiedGrowlInternalError) {
                    notifiedGrowlInternalError = true;
                    application.showError(NOTIFICATION_ERROR_TITLE,
                            "Growl had trouble handling notification. It may need restart or updating.");
                }
                break;
            }
        }

        @Override
        public void onCommunicationError(Throwable t) {
            logger.error("Error communicating with GNTP server", t);
            if (t instanceof ConnectException && growlConnectionRetries > GROWL_CONNECTION_ATTEMPTS
                    && !notifiedGrowlNotRunning) {
                notifiedGrowlNotRunning = true;
                application.showError(NOTIFICATION_ERROR_TITLE, "Growl is not running.");
            }
            growlConnectionRetries++;
        }
    }
}