diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 81ae2f0293..d8876570ee 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -267,6 +267,7 @@ ttf png gif + wav diff --git a/runelite-client/src/main/java/net/runelite/client/Notifier.java b/runelite-client/src/main/java/net/runelite/client/Notifier.java index 4f9653ed86..83cf8c0a3b 100644 --- a/runelite-client/src/main/java/net/runelite/client/Notifier.java +++ b/runelite-client/src/main/java/net/runelite/client/Notifier.java @@ -33,6 +33,9 @@ import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.TrayIcon; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; @@ -43,6 +46,13 @@ import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.inject.Singleton; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Clip; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.UnsupportedAudioFileException; +import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; @@ -61,6 +71,23 @@ import net.runelite.client.util.OSType; @Slf4j public class Notifier { + @Getter + @RequiredArgsConstructor + public enum NativeCustomOff + { + NATIVE("Native"), + CUSTOM("Custom"), + OFF("Off"); + + private final String name; + + @Override + public String toString() + { + return name; + } + } + // Default timeout of notification in milliseconds private static final int DEFAULT_TIMEOUT = 10000; private static final String DOUBLE_QUOTE = "\""; @@ -130,9 +157,13 @@ public class Notifier sendNotification(appName, message, type); } - if (runeLiteConfig.enableNotificationSound()) + switch (runeLiteConfig.notificationSound()) { - Toolkit.getDefaultToolkit().beep(); + case NATIVE: + Toolkit.getDefaultToolkit().beep(); + break; + case CUSTOM: + executorService.submit(this::playCustomSound); } if (runeLiteConfig.enableGameMessageNotification() && client.getGameState() == GameState.LOGGED_IN) @@ -369,4 +400,48 @@ public class Notifier return "normal"; } } + + private void playCustomSound() + { + Clip clip = null; + + // Try to load the user sound from ~/.runelite/notification.wav + File file = new File(RuneLite.RUNELITE_DIR, "notification.wav"); + if (file.exists()) + { + try + { + InputStream fileStream = new BufferedInputStream(new FileInputStream(file)); + try (AudioInputStream sound = AudioSystem.getAudioInputStream(fileStream)) + { + clip = AudioSystem.getClip(); + clip.open(sound); + } + } + catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) + { + clip = null; + log.warn("Unable to play notification sound", e); + } + } + + if (clip == null) + { + // Otherwise load from the classpath + InputStream fileStream = new BufferedInputStream(Notifier.class.getResourceAsStream("notification.wav")); + try (AudioInputStream sound = AudioSystem.getAudioInputStream(fileStream)) + { + clip = AudioSystem.getClip(); + clip.open(sound); + } + catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) + { + log.warn("Unable to play builtin notification sound", e); + + Toolkit.getDefaultToolkit().beep(); + return; + } + } + clip.start(); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java index 2c4cf3597a..287e0468b2 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java @@ -26,6 +26,7 @@ package net.runelite.client.config; import java.awt.Dimension; import net.runelite.api.Constants; +import net.runelite.client.Notifier; import net.runelite.client.ui.ContainableFrame; @ConfigGroup("runelite") @@ -155,13 +156,13 @@ public interface RuneLiteConfig extends Config @ConfigItem( keyName = "notificationSound", - name = "Enable sound on notifications", + name = "Notification sound", description = "Enables the playing of a beep sound when notifications are displayed", position = 22 ) - default boolean enableNotificationSound() + default Notifier.NativeCustomOff notificationSound() { - return true; + return Notifier.NativeCustomOff.NATIVE; } @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java index c33753d4a3..a888df3334 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java @@ -26,28 +26,33 @@ package net.runelite.client.plugins.devtools; import java.awt.GridLayout; +import java.awt.TrayIcon; import javax.inject.Inject; +import javax.swing.JButton; import javax.swing.JPanel; import net.runelite.api.Client; +import net.runelite.client.Notifier; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; class DevToolsPanel extends PluginPanel { private final Client client; + private final Notifier notifier; private final DevToolsPlugin plugin; private final WidgetInspector widgetInspector; private final VarInspector varInspector; @Inject - private DevToolsPanel(Client client, DevToolsPlugin plugin, WidgetInspector widgetInspector, VarInspector varInspector) + private DevToolsPanel(Client client, DevToolsPlugin plugin, WidgetInspector widgetInspector, VarInspector varInspector, Notifier notifier) { super(); this.client = client; this.plugin = plugin; this.widgetInspector = widgetInspector; this.varInspector = varInspector; + this.notifier = notifier; setBackground(ColorScheme.DARK_GRAY_COLOR); @@ -121,6 +126,13 @@ class DevToolsPanel extends PluginPanel container.add(plugin.getSoundEffects()); + final JButton notificationBtn = new JButton("Notification"); + notificationBtn.addActionListener(e -> + { + notifier.notify("Wow!", TrayIcon.MessageType.ERROR); + }); + container.add(notificationBtn); + return container; } } diff --git a/runelite-client/src/main/resources/net/runelite/client/notification.txt b/runelite-client/src/main/resources/net/runelite/client/notification.txt new file mode 100644 index 0000000000..e3a7245c61 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/notification.txt @@ -0,0 +1,2 @@ +Notification is cloud from +https://github.com/akx/Notifications \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/notification.wav b/runelite-client/src/main/resources/net/runelite/client/notification.wav new file mode 100644 index 0000000000..60321281c3 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/notification.wav differ