Use Growl in JavaFX

突然ですが、みなさん Growl 使ってますか?
Mac ユーザーには(最近では iPhone ユーザーにも?) 言わずと知れたアプリケーションですが...
最近、Windows 用も存在する事を知り、 会社でも愛用中!!。

この Growl、なんと、Java からもメッセージを 通知できるというではありませんか!
面倒な通知機能をアプリケーション毎に実装することなく利用できるなんて...

で! Java で出来るのであれば、JavaFX も...
と言うことで、早速試してみました。

ちなみに、Growl は以下で手に入ります。

Growl にメッセージを通知するためのライブラリはいくつかあるようですが...
今回は libgrowl を使ってみました。

libgrowl は GNTP というプロトコルGrowl と しゃべるらしいのですが...
残念ながら、本家 Mac の最新版 Growl 1.2 では、 その仕組みが利用できない...
次のメジャーリリースでは対応されるらしいので、 それを待つしかないようです。 (詳細はこちら)
しかし、それを待ってはいられないので、 今回は Windows 版で試すことに...

サンプル

今回のサンプルは 『 Async in JavaFX 1.2 Part 3 』 のサンプルをほぼそのまま利用して、
プログレスバーが 100% になると同時に Growl に通知してメッセージを出力するようにしてみました。
ファイルアップロードやダウンロードはこんなイメージになるのかな?


※ ネットワークにアクセスしますので、セキュリティの警告がでます。

サンプルコードは以下です。

Main.fx

Growl に通知するのはとっても簡単です。
GrowlTask#onDone で GrowlConnector#notify を呼び出すだけです。
たった数行追加するだけでこれだけの機能が得られるなんて...
いい時代になったなぁ...

import java.lang.Thread;

import javafx.async.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.HBox;
import javafx.scene.layout.LayoutInfo;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import com.bluepapa32.async.ActionTask;
import com.bluepapa32.async.ProgressTask;

import net.sf.libgrowl.Application;
import net.sf.libgrowl.GrowlConnector;
import net.sf.libgrowl.Notification;
import net.sf.libgrowl.NotificationType;

        
var task: Task;

Stage {
  title: "Use Growl in Javafx"
  width: 260
  height: 100
  scene: Scene {
    content: VBox {
      translateX: 10, translateY: 10
      spacing: 10
      content: [
        HBox {
          spacing: 5
          content:[
            ProgressBar {
                progress: bind task.percentDone / 100
                layoutInfo: LayoutInfo { width: 200 }
            }
            Label { text: bind "{task.percentDone as Integer} %"; }
          ]
        }
        HBox {
          spacing: 10
          content: [
            Button { text: "Start",  action: start,  disable: bind started() }
            Button { text: "Cancel", action: cancel, disable: bind not started() }
          ]
        }
      ]
    }
  }
}

function start(): Void {
    task = ProgressTask { task: GrowlTask {} }
    task.start();
}

function cancel(): Void {
    task.stop();
}

bound function started(): Boolean {
    task.started and not task.stopped and not task.done
}

class GrowlTask extends ActionTask {

    def COMPLETE = new NotificationType("COMPLETE", "Sample Complete");

    init {

        var application = new Application("Growl on JavaFX");
        application.setIcon("http://duke.kenai.com/iconSized/duke.gif");

        var growl = new GrowlConnector();
        growl.register(application, [ COMPLETE ]);

        action = function() {
            this.progress = 0;
            this.maxProgress = 100;
            for (progress in [1..100]) {
                Thread.sleep(50);
                this.progress = progress;
            }
        }
        onDone = function() {
            growl.notify(new Notification(
                                application, COMPLETE,
                                "Hello, World!!", "My name is Duke!"));
        }
    }
}