Skip to content

Commit f6c190d

Browse files
committed
Add Notify notifier
https://github.com/dorkbox/Notify Use: notifier.implementation = notify notifier.notify.position = CENTER (TOP_RIGHT by default) notifier.notify.darkstyle = true (false by default)
1 parent 82bcb55 commit f6c190d

File tree

9 files changed

+263
-4
lines changed

9 files changed

+263
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ Go to [Wiki](https://github.com/jcgay/send-notification/wiki) to read full confi
8787
| **[notifu](http://www.paralint.com/projects/notifu/index.html)** for Windows | ![notifu](http://jeanchristophegay.com/images/notifier.notifu.png) |
8888
| **AnyBar** for [OS X](https://github.com/tonsky/AnyBar) and [Linux](https://github.com/limpbrains/somebar) | ![anybar](http://jeanchristophegay.com/images/notifier.anybar_maven.png) |
8989
| **[Toaster](https://github.com/nels-o/toaster)** for Windows 8 | ![toaster](http://jeanchristophegay.com/images/notifier.toaster.png) |
90+
| **[Notify](https://github.com/dorkbox/Notify)** since Java 6 | ![Notify](http://jeanchristophegay.com/images/notifier.notify.png) |
9091

9192
# Build status
9293

send-notification/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@
6666
<artifactId>guava</artifactId>
6767
<version>18.0</version>
6868
</dependency>
69+
<dependency>
70+
<groupId>com.dorkbox</groupId>
71+
<artifactId>Notify</artifactId>
72+
<version>2.20</version>
73+
</dependency>
6974
<dependency>
7075
<groupId>org.codehaus.groovy</groupId>
7176
<artifactId>groovy-all</artifactId>

send-notification/src/main/java/fr/jcgay/notification/Icon.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import com.google.common.io.Closeables;
66

77
import javax.imageio.ImageIO;
8-
import java.awt.image.RenderedImage;
8+
import java.awt.image.BufferedImage;
99
import java.io.File;
1010
import java.io.FileOutputStream;
1111
import java.io.IOException;
@@ -33,7 +33,7 @@ public abstract class Icon {
3333
*/
3434
public abstract URL content();
3535

36-
public RenderedImage toRenderedImage() {
36+
public BufferedImage toImage() {
3737
try {
3838
return ImageIO.read(content());
3939
} catch (IOException e) {

send-notification/src/main/java/fr/jcgay/notification/NotifierProvider.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
import fr.jcgay.notification.notifier.notificationcenter.TerminalNotifierConfiguration;
1717
import fr.jcgay.notification.notifier.notifu.NotifuConfiguration;
1818
import fr.jcgay.notification.notifier.notifu.NotifuNotifier;
19+
import fr.jcgay.notification.notifier.notify.NotifyConfiguration;
20+
import fr.jcgay.notification.notifier.notify.NotifyNotifier;
1921
import fr.jcgay.notification.notifier.notifysend.NotifySendConfiguration;
2022
import fr.jcgay.notification.notifier.notifysend.NotifySendNotifier;
2123
import fr.jcgay.notification.notifier.pushbullet.PushbulletConfiguration;
@@ -47,6 +49,7 @@ class NotifierProvider {
4749
private static final ChosenNotifiers ANYBAR = ChosenNotifiers.from("anybar");
4850
private static final ChosenNotifiers SIMPLE_NOTIFICATION_CENTER = ChosenNotifiers.from("simplenc");
4951
private static final ChosenNotifiers TOASTER = ChosenNotifiers.from("toaster");
52+
private static final ChosenNotifiers NOTIFY = ChosenNotifiers.from("notify");
5053

5154
private final RuntimeExecutor executor = new RuntimeExecutor();
5255
private final OperatingSystem os;
@@ -102,6 +105,9 @@ public DiscoverableNotifier byName(ChosenNotifiers notifier, Properties properti
102105
if (TOASTER.equals(notifier)) {
103106
return new ToasterNotifier(ToasterConfiguration.create(properties), executor);
104107
}
108+
if (NOTIFY.equals(notifier)) {
109+
return new NotifyNotifier(application, NotifyConfiguration.create(properties));
110+
}
105111

106112
return DoNothingNotifier.doNothing();
107113
}

send-notification/src/main/java/fr/jcgay/notification/notifier/growl/GrowlNotifier.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public Notifier init() {
4646

4747
GntpApplicationInfo gApplication = Gntp.appInfo(application.name()).build();
4848
gNotification = Gntp.notificationInfo(gApplication, application.id())
49-
.icon(application.icon().toRenderedImage())
49+
.icon(application.icon().toImage())
5050
.build();
5151
Gntp clientBuilder = Gntp.client(gApplication)
5252
.onPort(configuration.port())
@@ -72,7 +72,7 @@ public void send(Notification notification) {
7272
if (isClientRegistered()) {
7373
GntpNotification success = Gntp.notification(gNotification, notification.title())
7474
.text(notification.message())
75-
.icon(notification.icon().toRenderedImage())
75+
.icon(notification.icon().toImage())
7676
.priority(toPriority(notification.level()))
7777
.build();
7878
try {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package fr.jcgay.notification.notifier.notify;
2+
3+
import com.google.auto.value.AutoValue;
4+
import dorkbox.notify.Pos;
5+
6+
import java.util.Properties;
7+
8+
@AutoValue
9+
public abstract class NotifyConfiguration {
10+
11+
private static final NotifyConfiguration DEFAULT = new AutoValue_NotifyConfiguration(Pos.TOP_RIGHT, false);
12+
13+
public abstract Pos position();
14+
15+
public abstract boolean withDarkStyle();
16+
17+
NotifyConfiguration() {
18+
// prevent external subclasses
19+
}
20+
21+
public static NotifyConfiguration byDefault() {
22+
return DEFAULT;
23+
}
24+
25+
public static NotifyConfiguration create(Properties properties) {
26+
if (properties == null) {
27+
return byDefault();
28+
}
29+
30+
return new AutoValue_NotifyConfiguration(
31+
Pos.valueOf(properties.getProperty("notifier.notify.position", DEFAULT.position().name())),
32+
Boolean.valueOf(properties.getProperty("notifier.notify.darkstyle", String.valueOf(DEFAULT.withDarkStyle())))
33+
);
34+
}
35+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package fr.jcgay.notification.notifier.notify;
2+
3+
import com.google.common.base.MoreObjects;
4+
import com.google.common.base.Objects;
5+
import dorkbox.notify.Notify;
6+
import fr.jcgay.notification.Application;
7+
import fr.jcgay.notification.DiscoverableNotifier;
8+
import fr.jcgay.notification.Notification;
9+
import fr.jcgay.notification.Notifier;
10+
import org.slf4j.Logger;
11+
12+
import static java.util.concurrent.TimeUnit.SECONDS;
13+
import static org.slf4j.LoggerFactory.getLogger;
14+
15+
public class NotifyNotifier implements DiscoverableNotifier {
16+
17+
private static final Logger LOGGER = getLogger(NotifyNotifier.class);
18+
19+
private final Application application;
20+
private final NotifyConfiguration configuration;
21+
22+
private boolean skipNotifications;
23+
24+
public NotifyNotifier(Application application, NotifyConfiguration configuration) {
25+
LOGGER.debug("Configuring notify: {}.", configuration);
26+
this.configuration = configuration;
27+
this.application = application;
28+
}
29+
30+
@Override
31+
public Notifier init() {
32+
if (isHeadless()) {
33+
skipNotifications = true;
34+
}
35+
return this;
36+
}
37+
38+
@Override
39+
public boolean tryInit() {
40+
init();
41+
return !skipNotifications;
42+
}
43+
44+
@Override
45+
public void send(Notification notification) {
46+
Notify notify = Notify.create()
47+
.title(notification.title())
48+
.text(notification.message())
49+
.graphic(notification.icon().toImage())
50+
.position(configuration.position())
51+
.hideAfter((int) (application.timeout() == -1 ? SECONDS.toMillis(3) : application.timeout()));
52+
53+
if (configuration.withDarkStyle()) {
54+
notify.darkStyle();
55+
}
56+
57+
notify.show();
58+
}
59+
60+
@Override
61+
public void close() {
62+
if (!skipNotifications) {
63+
try {
64+
Thread.sleep(application.timeout() == -1 ? SECONDS.toMillis(3) : application.timeout());
65+
} catch (InterruptedException e) {
66+
Thread.currentThread().interrupt();
67+
}
68+
}
69+
}
70+
71+
private static boolean isHeadless() {
72+
return "true".equals(System.getProperty("java.awt.headless"));
73+
}
74+
75+
@Override
76+
public boolean isPersistent() {
77+
return false;
78+
}
79+
80+
@Override
81+
public int hashCode() {
82+
return Objects.hashCode(application, configuration, skipNotifications);
83+
}
84+
85+
@Override
86+
public boolean equals(Object obj) {
87+
if (this == obj) {
88+
return true;
89+
}
90+
if (obj == null || getClass() != obj.getClass()) {
91+
return false;
92+
}
93+
final NotifyNotifier other = (NotifyNotifier) obj;
94+
return Objects.equal(this.application, other.application)
95+
&& Objects.equal(this.configuration, other.configuration)
96+
&& Objects.equal(this.skipNotifications, other.skipNotifications);
97+
}
98+
99+
@Override
100+
public String toString() {
101+
return MoreObjects.toStringHelper(this)
102+
.add("application", application)
103+
.add("configuration", configuration)
104+
.add("skipNotifications", skipNotifications)
105+
.toString();
106+
}
107+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package fr.jcgay.notification.notifier.notify
2+
3+
import spock.lang.Specification
4+
import spock.lang.Unroll
5+
6+
import static dorkbox.notify.Pos.*
7+
8+
class NotifyConfigurationSpec extends Specification {
9+
10+
@Unroll
11+
def "should build default configuration when properties are [#empty]"() {
12+
when:
13+
def result = NotifyConfiguration.create(empty)
14+
15+
then:
16+
result == NotifyConfiguration.byDefault()
17+
18+
where:
19+
empty << [null, new Properties()]
20+
}
21+
22+
@Unroll
23+
def "should build user configuration with position [#position]"() {
24+
when:
25+
def result = NotifyConfiguration.create(
26+
['notifier.notify.position': position.name()] as Properties
27+
)
28+
29+
then:
30+
result.position() == position
31+
result.withDarkStyle() == NotifyConfiguration.byDefault().withDarkStyle()
32+
33+
where:
34+
position << [TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, CENTER]
35+
}
36+
37+
@Unroll
38+
def "should build user configuration with dark style ? [#style]"() {
39+
when:
40+
def result = NotifyConfiguration.create(
41+
['notifier.notify.darkstyle': style as String] as Properties
42+
)
43+
44+
then:
45+
result.position() == NotifyConfiguration.byDefault().position()
46+
result.withDarkStyle() == style
47+
48+
where:
49+
style << [true, false]
50+
}
51+
52+
def "fail when position is not valid"() {
53+
when:
54+
NotifyConfiguration.create(
55+
['notifier.notify.position': 'unknown'] as Properties
56+
)
57+
58+
then:
59+
thrown(IllegalArgumentException)
60+
}
61+
62+
def "get lighter style when darkstyle is not a boolean"() {
63+
when:
64+
def result = NotifyConfiguration.create(
65+
['notifier.notify.darkstyle': 'maybe'] as Properties
66+
)
67+
68+
then:
69+
!result.withDarkStyle()
70+
}
71+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import fr.jcgay.notification.Application;
2+
import fr.jcgay.notification.Icon;
3+
import fr.jcgay.notification.Notification;
4+
import fr.jcgay.notification.Notifier;
5+
import fr.jcgay.notification.SendNotification;
6+
7+
import java.net.URL;
8+
9+
public class NotifyExample {
10+
11+
public static void main(String[] args) {
12+
URL icon = SystemTrayExample.class.getResource("/image/dialog-clean.png");
13+
14+
Application application = Application.builder()
15+
.id("notify-example")
16+
.name("Notify Example")
17+
.icon(Icon.create(icon, "app"))
18+
.build();
19+
20+
Notifier notifier = new SendNotification()
21+
.setApplication(application)
22+
.setChosenNotifier("notify")
23+
.initNotifier();
24+
25+
Notification notification = Notification.builder()
26+
.title("Notify Notification")
27+
.message("Hello !")
28+
.icon(Icon.create(icon, "ok"))
29+
.build();
30+
31+
notifier.send(notification);
32+
notifier.close();
33+
}
34+
}

0 commit comments

Comments
 (0)