diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml
index 4bae77ec9e..caea2fc76b 100644
--- a/runelite-client/pom.xml
+++ b/runelite-client/pom.xml
@@ -75,17 +75,6 @@
gson
2.4
-
- fr.jcgay.send-notification
- send-notification
- 0.14.0
-
-
- com.squareup.okhttp
- okhttp
-
-
-
org.pushingpixels
substance
diff --git a/runelite-client/src/main/java/net/runelite/client/Notifier.java b/runelite-client/src/main/java/net/runelite/client/Notifier.java
new file mode 100644
index 0000000000..393448493c
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/Notifier.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2016-2017, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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 net.runelite.client;
+
+import com.google.common.escape.Escaper;
+import com.google.common.escape.Escapers;
+import java.awt.TrayIcon;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class Notifier
+{
+ private static enum OSType
+ {
+ Windows, MacOS, Linux, Other
+ };
+
+ private static final String DOUBLE_QUOTE = "\"";
+ private static final Escaper SHELL_ESCAPE;
+ private static final OSType DETECTED_OS;
+
+ static
+ {
+ final Escapers.Builder builder = Escapers.builder();
+ builder.addEscape('"', "'");
+ SHELL_ESCAPE = builder.build();
+
+ final String OS = System
+ .getProperty("os.name", "generic")
+ .toLowerCase();
+
+ if ((OS.contains("mac")) || (OS.contains("darwin")))
+ {
+ DETECTED_OS = OSType.MacOS;
+ }
+ else if (OS.contains("win"))
+ {
+ DETECTED_OS = OSType.Windows;
+ }
+ else if (OS.contains("nux"))
+ {
+ DETECTED_OS = OSType.Linux;
+ }
+ else
+ {
+ DETECTED_OS = OSType.Other;
+ }
+
+ log.debug("Detect OS: {}", DETECTED_OS);
+ }
+
+ private final TrayIcon trayIcon;
+
+ public Notifier(final TrayIcon trayIcon)
+ {
+ this.trayIcon = trayIcon;
+ }
+
+ public void sendNotification(
+ final String title,
+ final String message,
+ final TrayIcon.MessageType type,
+ final String subtitle)
+ {
+ final String escapedTitle = SHELL_ESCAPE.escape(title);
+ final String escapedMessage = SHELL_ESCAPE.escape(message);
+ final String escapedSubtitle = subtitle != null ? SHELL_ESCAPE.escape(subtitle) : null;
+
+ switch (DETECTED_OS)
+ {
+ case Linux:
+ sendLinuxNotification(escapedTitle, escapedMessage, type);
+ break;
+ case MacOS:
+ sendMacNotification(escapedTitle, escapedMessage, escapedSubtitle);
+ break;
+ default:
+ sendTrayNotification(title, message, type);
+ }
+ }
+
+ private void sendTrayNotification(
+ final String title,
+ final String message,
+ final TrayIcon.MessageType type)
+ {
+ if (trayIcon != null)
+ {
+ trayIcon.displayMessage(title, message, type);
+ }
+ }
+
+ private void sendLinuxNotification(
+ final String title,
+ final String message,
+ final TrayIcon.MessageType type)
+ {
+ final List commands = new ArrayList<>();
+ commands.add("notify-send");
+ commands.add(title);
+ commands.add(message);
+ commands.add("-u");
+ commands.add(toUrgency(type));
+ sendCommand(commands);
+ }
+
+ private void sendMacNotification(
+ final String title,
+ final String message,
+ final String subtitle)
+ {
+ final List commands = new ArrayList<>();
+ commands.add("osascript");
+ commands.add("-e");
+
+ final StringBuilder script = new StringBuilder("display notification ");
+
+ script.append(DOUBLE_QUOTE)
+ .append(message)
+ .append(DOUBLE_QUOTE);
+
+ script.append(" with title ")
+ .append(DOUBLE_QUOTE)
+ .append(title)
+ .append(DOUBLE_QUOTE);
+
+ if (subtitle != null)
+ {
+ script.append(" subtitle ")
+ .append(DOUBLE_QUOTE)
+ .append(subtitle)
+ .append(DOUBLE_QUOTE);
+ }
+
+ commands.add(script.toString());
+ sendCommand(commands);
+ }
+
+ private void sendCommand(final List commands)
+ {
+ try
+ {
+ new ProcessBuilder(commands.toArray(new String[commands.size()]))
+ .redirectErrorStream(true)
+ .start();
+ }
+ catch (IOException ex)
+ {
+ log.warn(null, ex);
+ }
+ }
+
+ private static String toUrgency(TrayIcon.MessageType type)
+ {
+ switch (type)
+ {
+ case WARNING:
+ case ERROR:
+ return "critical";
+ default:
+ return "normal";
+ }
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java
index c96fb1297e..3fce666eb2 100644
--- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java
+++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java
@@ -52,12 +52,6 @@ import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
-
-import fr.jcgay.notification.Application;
-import fr.jcgay.notification.Icon;
-import fr.jcgay.notification.Notification;
-import fr.jcgay.notification.Notifier;
-import fr.jcgay.notification.SendNotification;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import lombok.extern.slf4j.Slf4j;
@@ -84,7 +78,6 @@ public class RuneLite
public static final String APP_NAME = "RuneLite";
public static Image ICON;
- public static Icon NOTIFY_ICON;
private static Injector injector;
@@ -117,14 +110,12 @@ public class RuneLite
private Notifier notifier;
-
static
{
try
{
final URL icon = ClientUI.class.getResource("/runelite.png");
ICON = ImageIO.read(icon.openStream());
- NOTIFY_ICON = Icon.create(icon, APP_NAME);
}
catch (IOException ex)
{
@@ -177,14 +168,7 @@ public class RuneLite
eventBus.register(menuManager);
// Setup the notifier
- notifier = new SendNotification()
- .setApplication(Application
- .builder()
- .icon(NOTIFY_ICON)
- .name(APP_NAME)
- .id(APP_NAME)
- .build())
- .initNotifier();
+ notifier = new Notifier(trayIcon);
// Load the plugins, but does not start them yet.
// This will initialize configuration
@@ -414,18 +398,7 @@ public class RuneLite
public void notify(String message, TrayIcon.MessageType type)
{
- final Notification.Level notificationLevel = Notification.Level
- .valueOf((TrayIcon.MessageType.NONE.equals(type)
- ? TrayIcon.MessageType.INFO
- : type).toString());
-
- notifier.send(Notification.builder()
- .title(APP_NAME)
- .message(message)
- .icon(NOTIFY_ICON)
- .level(notificationLevel)
- .build());
-
+ notifier.sendNotification(APP_NAME, message, type, null);
Toolkit.getDefaultToolkit().beep();
}