From 888046676f2125af5037f9dc8699545677840573 Mon Sep 17 00:00:00 2001 From: 0xboobface <0xboobface@gmail.com> Date: Sun, 9 Dec 2018 18:46:33 +0100 Subject: [PATCH] Add first configurable version of the notification system --- .../java/ctbrec/ui/CamrecApplication.java | 53 +--- .../src/main/java/ctbrec/ui/JavaFxModel.java | 5 + .../main/java/ctbrec/ui/controls/Wizard.java | 34 ++- .../ui/event/ModelStateNotification.java | 23 -- .../main/java/ctbrec/ui/event/PlaySound.java | 10 + .../ctbrec/ui/event/ShowNotification.java | 42 +++ .../ui/settings/ActionSettingsPanel.java | 252 ++++++++++++++++-- .../ctbrec/ui/settings/ListSelectionPane.java | 121 +++++++++ .../java/ctbrec/ui/settings/SettingsTab.java | 7 +- .../src/main/java/ctbrec/AbstractModel.java | 8 + common/src/main/java/ctbrec/Model.java | 2 +- common/src/main/java/ctbrec/Settings.java | 5 +- common/src/main/java/ctbrec/event/Action.java | 9 + .../java/ctbrec/event/EventBusHolder.java | 31 ++- .../main/java/ctbrec/event/EventHandler.java | 125 ++++++++- .../event/EventHandlerConfiguration.java | 78 +++++- .../java/ctbrec/event/EventPredicate.java | 10 + .../java/ctbrec/event/EventTypePredicate.java | 15 +- .../java/ctbrec/event/ExecuteProgram.java | 53 ++++ .../java/ctbrec/event/ModelPredicate.java | 53 +++- .../ctbrec/event/ModelStatePredicate.java | 17 +- .../event/RecordingStateChangedEvent.java | 4 + .../ctbrec/event/RecordingStatePredicate.java | 31 +++ 23 files changed, 838 insertions(+), 150 deletions(-) delete mode 100644 client/src/main/java/ctbrec/ui/event/ModelStateNotification.java create mode 100644 client/src/main/java/ctbrec/ui/event/ShowNotification.java create mode 100644 client/src/main/java/ctbrec/ui/settings/ListSelectionPane.java create mode 100644 common/src/main/java/ctbrec/event/EventPredicate.java create mode 100644 common/src/main/java/ctbrec/event/ExecuteProgram.java create mode 100644 common/src/main/java/ctbrec/event/RecordingStatePredicate.java diff --git a/client/src/main/java/ctbrec/ui/CamrecApplication.java b/client/src/main/java/ctbrec/ui/CamrecApplication.java index df11cabc..40ca2431 100644 --- a/client/src/main/java/ctbrec/ui/CamrecApplication.java +++ b/client/src/main/java/ctbrec/ui/CamrecApplication.java @@ -1,9 +1,6 @@ package ctbrec.ui; -import static ctbrec.Model.State.*; -import static ctbrec.event.Event.Type.*; - import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; @@ -19,18 +16,16 @@ import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.eventbus.Subscribe; import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.Moshi; import com.squareup.moshi.Types; import ctbrec.Config; -import ctbrec.Model; import ctbrec.StringUtil; import ctbrec.Version; -import ctbrec.event.Event; import ctbrec.event.EventBusHolder; -import ctbrec.event.ModelStateChangedEvent; +import ctbrec.event.EventHandler; +import ctbrec.event.EventHandlerConfiguration; import ctbrec.io.HttpClient; import ctbrec.recorder.LocalRecorder; import ctbrec.recorder.OnlineMonitor; @@ -62,7 +57,6 @@ public class CamrecApplication extends Application { static final transient Logger LOG = LoggerFactory.getLogger(CamrecApplication.class); - private Stage primaryStage; private Config config; private Recorder recorder; private OnlineMonitor onlineMonitor; @@ -75,15 +69,14 @@ public class CamrecApplication extends Application { @Override public void start(Stage primaryStage) throws Exception { - this.primaryStage = primaryStage; logEnvironment(); - registerAlertSystem(); sites.add(new BongaCams()); sites.add(new Cam4()); sites.add(new Camsoda()); sites.add(new Chaturbate()); sites.add(new MyFreeCams()); loadConfig(); + registerAlertSystem(); createHttpClient(); hostServices = getHostServices(); createRecorder(); @@ -101,7 +94,6 @@ public class CamrecApplication extends Application { } createGui(primaryStage); checkForUpdates(); - } private void logEnvironment() { @@ -134,7 +126,7 @@ public class CamrecApplication extends Application { rootPane.getTabs().add(modelsTab); RecordingsTab recordingsTab = new RecordingsTab("Recordings", recorder, config, sites); rootPane.getTabs().add(recordingsTab); - settingsTab = new SettingsTab(sites); + settingsTab = new SettingsTab(sites, recorder); rootPane.getTabs().add(settingsTab); rootPane.getTabs().add(new DonateTabFx()); @@ -222,38 +214,11 @@ public class CamrecApplication extends Application { // } catch (InterruptedException e) { // e.printStackTrace(); // } - EventBusHolder.BUS.register(new Object() { - @Subscribe - public void modelEvent(Event e) { - try { - if (e.getType() == MODEL_STATUS_CHANGED) { - ModelStateChangedEvent evt = (ModelStateChangedEvent) e; - Model model = evt.getModel(); - if (evt.getNewState() == ONLINE && primaryStage != null && primaryStage.getTitle() != null) { - String header = "Model Online"; - String msg = model.getDisplayName() + " is now online"; - LOG.debug(msg); - //OS.notification(primaryStage.getTitle(), header, msg); - } - } - } catch (Exception e1) { - LOG.error("Couldn't show notification", e1); - } - } - }); - - // EventBusHolder.BUS.register(new Object() { - // URL url = CamrecApplication.class.getResource("/Oxygen-Im-Highlight-Msg.mp3"); - // PlaySound playSound = new PlaySound(url); - // EventHandler reaction = new EventHandler(playSound); - // // LogReaction reaction = new LogReaction(); - // @Subscribe - // public void modelEvent(Event e) { - // reaction.reactToEvent(e); - // } - // }); - - + for (EventHandlerConfiguration config : Config.getInstance().getSettings().eventHandlers) { + EventHandler handler = new EventHandler(config); + EventBusHolder.register(handler); + LOG.debug("Registered event handler for {} {}", config.getEvent(), config.getName()); + } LOG.debug("Alert System registered"); } diff --git a/client/src/main/java/ctbrec/ui/JavaFxModel.java b/client/src/main/java/ctbrec/ui/JavaFxModel.java index 63193d69..0f9019d4 100644 --- a/client/src/main/java/ctbrec/ui/JavaFxModel.java +++ b/client/src/main/java/ctbrec/ui/JavaFxModel.java @@ -207,4 +207,9 @@ public class JavaFxModel implements Model { public void setDisplayName(String name) { delegate.setDisplayName(name); } + + @Override + public int compareTo(Model o) { + return delegate.compareTo(o); + } } diff --git a/client/src/main/java/ctbrec/ui/controls/Wizard.java b/client/src/main/java/ctbrec/ui/controls/Wizard.java index bc653e2e..c065bb06 100644 --- a/client/src/main/java/ctbrec/ui/controls/Wizard.java +++ b/client/src/main/java/ctbrec/ui/controls/Wizard.java @@ -1,5 +1,8 @@ package ctbrec.ui.controls; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; @@ -11,6 +14,7 @@ import javafx.stage.Stage; public class Wizard extends BorderPane { + private static final transient Logger LOG = LoggerFactory.getLogger(Wizard.class); private Pane[] pages; private StackPane stack; private Stage stage; @@ -18,6 +22,7 @@ public class Wizard extends BorderPane { private Button next; private Button prev; private Button finish; + private boolean cancelled = true; public Wizard(Stage stage, Pane... pages) { this.stage = stage; @@ -33,42 +38,55 @@ public class Wizard extends BorderPane { private void createUi() { stack = new StackPane(); setCenter(stack); - + next = new Button("Next"); next.setOnAction(evt -> nextPage()); - prev = new Button("Prev"); + prev = new Button("Back"); prev.setOnAction(evt -> prevPage()); + prev.visibleProperty().bind(next.visibleProperty()); + next.setVisible(pages.length > 1); Button cancel = new Button("Cancel"); cancel.setOnAction(evt -> stage.close()); finish = new Button("Finish"); + finish.setOnAction(evt -> { + cancelled = false; + stage.close(); + }); HBox buttons = new HBox(5, prev, next, cancel, finish); buttons.setAlignment(Pos.BASELINE_RIGHT); setBottom(buttons); - BorderPane.setMargin(buttons, new Insets(5)); + BorderPane.setMargin(buttons, new Insets(10)); if (pages.length != 0) { - stack.getChildren().add(pages[0]); + prevPage(); } - setButtonStates(); } private void prevPage() { page = Math.max(0, --page); stack.getChildren().clear(); stack.getChildren().add(pages[page]); - setButtonStates(); + updateState(); } private void nextPage() { page = Math.min(pages.length - 1, ++page); stack.getChildren().clear(); stack.getChildren().add(pages[page]); - setButtonStates(); + updateState(); } - private void setButtonStates() { + private void updateState() { prev.setDisable(page == 0); next.setDisable(page == pages.length - 1); finish.setDisable(page != pages.length - 1); + LOG.debug("Setting border"); + pages[page].setStyle( + "-fx-background-color: -fx-inner-border, -fx-background;"+ + "-fx-background-insets: 0 0 -1 0, 0, 1, 2;"); + } + + public boolean isCancelled() { + return cancelled; } } diff --git a/client/src/main/java/ctbrec/ui/event/ModelStateNotification.java b/client/src/main/java/ctbrec/ui/event/ModelStateNotification.java deleted file mode 100644 index a831b4e3..00000000 --- a/client/src/main/java/ctbrec/ui/event/ModelStateNotification.java +++ /dev/null @@ -1,23 +0,0 @@ -package ctbrec.ui.event; - -import ctbrec.OS; -import ctbrec.event.Action; -import ctbrec.event.Event; -import ctbrec.ui.CamrecApplication; - -public class ModelStateNotification extends Action { - - private String header; - private String msg; - - public ModelStateNotification(String header, String msg) { - this.header = header; - this.msg = msg; - name = "show notification"; - } - - @Override - public void accept(Event evt) { - OS.notification(CamrecApplication.title, header, msg); - } -} diff --git a/client/src/main/java/ctbrec/ui/event/PlaySound.java b/client/src/main/java/ctbrec/ui/event/PlaySound.java index 7464bebb..c23d77cd 100644 --- a/client/src/main/java/ctbrec/ui/event/PlaySound.java +++ b/client/src/main/java/ctbrec/ui/event/PlaySound.java @@ -1,15 +1,19 @@ package ctbrec.ui.event; +import java.io.File; import java.net.URL; import ctbrec.event.Action; import ctbrec.event.Event; +import ctbrec.event.EventHandlerConfiguration.ActionConfiguration; import javafx.scene.media.AudioClip; public class PlaySound extends Action { private URL url; + public PlaySound() {} + public PlaySound(URL url) { this.url = url; name = "play sound"; @@ -20,4 +24,10 @@ public class PlaySound extends Action { AudioClip clip = new AudioClip(url.toString()); clip.play(); } + + @Override + public void configure(ActionConfiguration config) throws Exception { + File file = new File((String) config.getConfiguration().get("file")); + url = file.toURI().toURL(); + } } diff --git a/client/src/main/java/ctbrec/ui/event/ShowNotification.java b/client/src/main/java/ctbrec/ui/event/ShowNotification.java new file mode 100644 index 00000000..4d91350d --- /dev/null +++ b/client/src/main/java/ctbrec/ui/event/ShowNotification.java @@ -0,0 +1,42 @@ +package ctbrec.ui.event; + +import ctbrec.Model; +import ctbrec.OS; +import ctbrec.event.Action; +import ctbrec.event.Event; +import ctbrec.event.EventHandlerConfiguration.ActionConfiguration; +import ctbrec.event.ModelStateChangedEvent; +import ctbrec.event.RecordingStateChangedEvent; +import ctbrec.ui.CamrecApplication; + +public class ShowNotification extends Action { + + public ShowNotification() { + name = "show notification"; + } + + @Override + public void accept(Event evt) { + String header = evt.getType().toString(); + String msg; + switch(evt.getType()) { + case MODEL_STATUS_CHANGED: + ModelStateChangedEvent modelEvent = (ModelStateChangedEvent) evt; + Model m = modelEvent.getModel(); + msg = m.getDisplayName() + " is now " + modelEvent.getNewState().toString(); + break; + case RECORDING_STATUS_CHANGED: + RecordingStateChangedEvent recEvent = (RecordingStateChangedEvent) evt; + m = recEvent.getModel(); + msg = "Recording for model " + m.getDisplayName() + " is now in state " + recEvent.getState().toString(); + break; + default: + msg = evt.getDescription(); + } + OS.notification(CamrecApplication.title, header, msg); + } + + @Override + public void configure(ActionConfiguration config) throws Exception { + } +} diff --git a/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java b/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java index 250bce86..a143d181 100644 --- a/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java +++ b/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java @@ -1,35 +1,95 @@ package ctbrec.ui.settings; +import java.io.File; import java.io.InputStream; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ctbrec.Config; +import ctbrec.Model; +import ctbrec.OS; +import ctbrec.Recording; +import ctbrec.event.Event; +import ctbrec.event.EventBusHolder; +import ctbrec.event.EventHandler; import ctbrec.event.EventHandlerConfiguration; +import ctbrec.event.EventHandlerConfiguration.ActionConfiguration; +import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration; +import ctbrec.event.ExecuteProgram; +import ctbrec.event.ModelPredicate; +import ctbrec.event.ModelStatePredicate; +import ctbrec.event.RecordingStatePredicate; +import ctbrec.recorder.Recorder; +import ctbrec.ui.CamrecApplication; +import ctbrec.ui.controls.FileSelectionBox; +import ctbrec.ui.controls.ProgramSelectionBox; import ctbrec.ui.controls.Wizard; +import ctbrec.ui.event.PlaySound; +import ctbrec.ui.event.ShowNotification; +import javafx.collections.ListChangeListener; import javafx.event.ActionEvent; import javafx.geometry.Insets; +import javafx.geometry.Orientation; +import javafx.geometry.VPos; +import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ComboBox; import javafx.scene.control.Label; +import javafx.scene.control.ListView; import javafx.scene.control.ScrollPane; -import javafx.scene.control.TableView; +import javafx.scene.control.SelectionMode; +import javafx.scene.control.Separator; import javafx.scene.control.TextField; import javafx.scene.control.TitledPane; import javafx.scene.image.Image; -import javafx.scene.layout.Border; import javafx.scene.layout.BorderPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.stage.Modality; import javafx.stage.Stage; +import javafx.stage.Window; public class ActionSettingsPanel extends TitledPane { + private static final transient Logger LOG = LoggerFactory.getLogger(ActionSettingsPanel.class); + private ListView actionTable; - private TableView actionTable; + private TextField name = new TextField(); + private ComboBox event = new ComboBox<>(); + private ComboBox modelState = new ComboBox<>(); + private ComboBox recordingState = new ComboBox<>(); - public ActionSettingsPanel(SettingsTab settingsTab) { + private CheckBox playSound = new CheckBox("Play sound"); + private FileSelectionBox sound = new FileSelectionBox(); + private CheckBox showNotification = new CheckBox("Notify me"); + private Button testNotification = new Button("Test"); + private CheckBox executeProgram = new CheckBox("Execute program"); + private ProgramSelectionBox program = new ProgramSelectionBox(); + private ListSelectionPane modelSelectionPane; + + private Recorder recorder; + + public ActionSettingsPanel(SettingsTab settingsTab, Recorder recorder) { + this.recorder = recorder; setText("Events & Actions"); setExpanded(true); setCollapsible(false); createGui(); + loadEventHandlers(); + } + + private void loadEventHandlers() { + actionTable.getItems().addAll(Config.getInstance().getSettings().eventHandlers); } private void createGui() { @@ -37,58 +97,202 @@ public class ActionSettingsPanel extends TitledPane { setContent(mainLayout); actionTable = createActionTable(); - actionTable.setPrefSize(300, 200); ScrollPane scrollPane = new ScrollPane(actionTable); scrollPane.setFitToHeight(true); scrollPane.setFitToWidth(true); - scrollPane.setBorder(Border.EMPTY); + scrollPane.setStyle("-fx-background-color: -fx-background"); mainLayout.setCenter(scrollPane); - BorderPane.setMargin(scrollPane, new Insets(5)); Button add = new Button("Add"); add.setOnAction(this::add); Button delete = new Button("Delete"); delete.setOnAction(this::delete); delete.setDisable(true); - HBox buttons = new HBox(10, add, delete); + HBox buttons = new HBox(5, add, delete); mainLayout.setBottom(buttons); - BorderPane.setMargin(buttons, new Insets(5)); + BorderPane.setMargin(buttons, new Insets(5, 0, 0, 0)); + + actionTable.getSelectionModel().getSelectedItems().addListener(new ListChangeListener() { + @Override + public void onChanged(Change change) { + delete.setDisable(change.getList().isEmpty()); + } + }); } private void add(ActionEvent evt) { - EventHandlerConfiguration config = new EventHandlerConfiguration(); - Pane namePane = createNamePane(config); - GridPane pane2 = SettingsTab.createGridLayout(); - pane2.add(new Label("Pane 2"), 0, 0); - GridPane pane3 = SettingsTab.createGridLayout(); - pane3.add(new Label("Pane 3"), 0, 0); + Pane actionPane = createActionPane(); Stage dialog = new Stage(); + dialog.initModality(Modality.APPLICATION_MODAL); + dialog.initOwner(getScene().getWindow()); dialog.setTitle("New Action"); InputStream icon = getClass().getResourceAsStream("/icon.png"); dialog.getIcons().add(new Image(icon)); - Wizard root = new Wizard(dialog, namePane, pane2, pane3); - Scene scene = new Scene(root, 640, 480); + Wizard root = new Wizard(dialog, actionPane); + Scene scene = new Scene(root, 800, 540); scene.getStylesheets().addAll(getScene().getStylesheets()); dialog.setScene(scene); + centerOnParent(dialog); dialog.showAndWait(); + if(!root.isCancelled()) { + createEventHandler(); + } + } + + private void createEventHandler() { + EventHandlerConfiguration config = new EventHandlerConfiguration(); + config.setName(name.getText()); + config.setEvent(event.getValue()); + if(event.getValue() == Event.Type.MODEL_STATUS_CHANGED) { + PredicateConfiguration pc = new PredicateConfiguration(); + pc.setType(ModelStatePredicate.class.getName()); + pc.getConfiguration().put("state", modelState.getValue().name()); + pc.setName("state = " + modelState.getValue().toString()); + config.getPredicates().add(pc); + } else if(event.getValue() == Event.Type.RECORDING_STATUS_CHANGED) { + PredicateConfiguration pc = new PredicateConfiguration(); + pc.setType(RecordingStatePredicate.class.getName()); + pc.getConfiguration().put("state", recordingState.getValue().name()); + pc.setName("state = " + recordingState.getValue().toString()); + config.getPredicates().add(pc); + } + if(!modelSelectionPane.isAllSelected()) { + PredicateConfiguration pc = new PredicateConfiguration(); + pc.setType(ModelPredicate.class.getName()); + pc.setModels(modelSelectionPane.getSelectedItems()); + pc.setName("model is one of:" + modelSelectionPane.getSelectedItems()); + config.getPredicates().add(pc); + } + if(showNotification.isSelected()) { + ActionConfiguration ac = new ActionConfiguration(); + ac.setType(ShowNotification.class.getName()); + ac.setName("show notification"); + config.getActions().add(ac); + } + if(playSound.isSelected()) { + ActionConfiguration ac = new ActionConfiguration(); + ac.setType(PlaySound.class.getName()); + File file = sound.fileProperty().get(); + ac.getConfiguration().put("file", file.getAbsolutePath()); + ac.setName("play " + file.getName()); + config.getActions().add(ac); + } + if(executeProgram.isSelected()) { + ActionConfiguration ac = new ActionConfiguration(); + ac.setType(ExecuteProgram.class.getName()); + File file = program.fileProperty().get(); + ac.getConfiguration().put("file", file.getAbsolutePath()); + ac.setName("execute " + file.getName()); + config.getActions().add(ac); + } + + EventHandler handler = new EventHandler(config); + EventBusHolder.register(handler); + Config.getInstance().getSettings().eventHandlers.add(config); + actionTable.getItems().add(config); + LOG.debug("Registered event handler for {} {}", config.getEvent(), config.getName()); } private void delete(ActionEvent evt) { - + List selected = new ArrayList<>(actionTable.getSelectionModel().getSelectedItems()); + for (EventHandlerConfiguration config : selected) { + EventBusHolder.unregister(config.getId()); + Config.getInstance().getSettings().eventHandlers.remove(config); + actionTable.getItems().remove(config); + } } - private Pane createNamePane(EventHandlerConfiguration config) { + private Pane createActionPane() { GridPane layout = SettingsTab.createGridLayout(); + recordingState.prefWidthProperty().bind(event.widthProperty()); + modelState.prefWidthProperty().bind(event.widthProperty()); + name.prefWidthProperty().bind(event.widthProperty()); + int row = 0; layout.add(new Label("Name"), 0, row); - TextField name = new TextField(); - layout.add(name, 1, row); + layout.add(name, 1, row++); + + layout.add(new Label("Event"), 0, row); + event.getItems().add(Event.Type.MODEL_STATUS_CHANGED); + event.getItems().add(Event.Type.RECORDING_STATUS_CHANGED); + event.setOnAction(evt -> { + modelState.setVisible(event.getSelectionModel().getSelectedItem() == Event.Type.MODEL_STATUS_CHANGED); + }); + event.getSelectionModel().select(Event.Type.MODEL_STATUS_CHANGED); + layout.add(event, 1, row++); + + layout.add(new Label("State"), 0, row); + modelState.getItems().addAll(Model.State.values()); + layout.add(modelState, 1, row); + recordingState.getItems().addAll(Recording.State.values()); + layout.add(recordingState, 1, row++); + recordingState.visibleProperty().bind(modelState.visibleProperty().not()); + + layout.add(createSeparator(), 0, row++); + + Label l = new Label("Models"); + layout.add(l, 0, row); + modelSelectionPane = new ListSelectionPane(recorder.getModelsRecording(), Collections.emptyList()); + layout.add(modelSelectionPane, 1, row++); + GridPane.setValignment(l, VPos.TOP); + GridPane.setHgrow(modelSelectionPane, Priority.ALWAYS); + GridPane.setFillWidth(modelSelectionPane, true); + + layout.add(createSeparator(), 0, row++); + + layout.add(showNotification, 0, row); + layout.add(testNotification, 1, row++); + testNotification.setOnAction(evt -> { + DateTimeFormatter format = DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM); + ZonedDateTime time = ZonedDateTime.now(); + OS.notification(CamrecApplication.title, "Test Notification", "Oi, what's up! " + format.format(time)); + }); + testNotification.disableProperty().bind(showNotification.selectedProperty().not()); + + layout.add(playSound, 0, row); + layout.add(sound, 1, row++); + sound.disableProperty().bind(playSound.selectedProperty().not()); + + layout.add(executeProgram, 0, row); + layout.add(program, 1, row); + program.disableProperty().bind(executeProgram.selectedProperty().not()); + + GridPane.setFillWidth(name, true); + GridPane.setHgrow(name, Priority.ALWAYS); + GridPane.setFillWidth(sound, true); + return layout; } - - private TableView createActionTable() { - TableView view = new TableView(); + private ListView createActionTable() { + ListView view = new ListView<>(); + view.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); + view.setPrefSize(300, 200); return view; } + + private Node createSeparator() { + Separator divider = new Separator(Orientation.HORIZONTAL); + GridPane.setHgrow(divider, Priority.ALWAYS); + GridPane.setFillWidth(divider, true); + GridPane.setColumnSpan(divider, 2); + int tb = 20; + int lr = 0; + GridPane.setMargin(divider, new Insets(tb, lr, tb, lr)); + return divider; + } + + private void centerOnParent(Stage dialog) { + dialog.setWidth(dialog.getScene().getWidth()); + dialog.setHeight(dialog.getScene().getHeight()); + double w = dialog.getWidth(); + double h = dialog.getHeight(); + Window p = dialog.getOwner(); + double px = p.getX(); + double py = p.getY(); + double pw = p.getWidth(); + double ph = p.getHeight(); + dialog.setX(px + (pw - w) / 2); + dialog.setY(py + (ph - h) / 2); + } } diff --git a/client/src/main/java/ctbrec/ui/settings/ListSelectionPane.java b/client/src/main/java/ctbrec/ui/settings/ListSelectionPane.java new file mode 100644 index 00000000..e5cd9dc1 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/settings/ListSelectionPane.java @@ -0,0 +1,121 @@ +package ctbrec.ui.settings; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; +import javafx.scene.control.Label; +import javafx.scene.control.ListView; +import javafx.scene.control.SelectionMode; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; + +public class ListSelectionPane> extends GridPane { + + private ListView availableListView = new ListView<>(); + private ListView selectedListView = new ListView<>(); + private Button addModel = new Button(">"); + private Button removeModel = new Button("<"); + private CheckBox selectAll = new CheckBox("all"); + + public ListSelectionPane(List available, List selected) { + super(); + setHgap(5); + setVgap(5); + + createGui(); + fillLists(available, selected); + } + + private void fillLists(List available, List selected) { + ObservableList obsAvail = FXCollections.observableArrayList(available); + ObservableList obsSel = FXCollections.observableArrayList(selected); + for (Iterator iterator = obsAvail.iterator(); iterator.hasNext();) { + T t = iterator.next(); + if(obsSel.contains(t)) { + iterator.remove(); + } + } + Collections.sort(obsAvail); + Collections.sort(obsSel); + availableListView.setItems(obsAvail); + selectedListView.setItems(obsSel); + availableListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); + selectedListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); + } + + private void createGui() { + Label labelAvailable = new Label("Available"); + Label labelSelected = new Label("Selected"); + + add(labelAvailable, 0, 0); + add(availableListView, 0, 1); + + VBox buttonBox = new VBox(5); + buttonBox.getChildren().add(addModel); + buttonBox.getChildren().add(removeModel); + buttonBox.setAlignment(Pos.CENTER); + add(buttonBox, 1, 1); + + add(labelSelected, 2, 0); + add(selectedListView, 2, 1); + + add(selectAll, 0, 2); + + GridPane.setHgrow(availableListView, Priority.ALWAYS); + GridPane.setHgrow(selectedListView, Priority.ALWAYS); + GridPane.setFillWidth(availableListView, true); + GridPane.setFillWidth(selectedListView, true); + + addModel.setOnAction(evt -> addSelectedItems()); + removeModel.setOnAction(evt -> removeSelectedItems()); + + availableListView.disableProperty().bind(selectAll.selectedProperty()); + selectedListView.disableProperty().bind(selectAll.selectedProperty()); + addModel.disableProperty().bind(selectAll.selectedProperty()); + removeModel.disableProperty().bind(selectAll.selectedProperty()); + } + + private void addSelectedItems() { + List selected = new ArrayList<>(availableListView.getSelectionModel().getSelectedItems()); + for (T t : selected) { + if(!selectedListView.getItems().contains(t)) { + selectedListView.getItems().add(t); + availableListView.getItems().remove(t); + } + } + Collections.sort(selectedListView.getItems()); + } + + private void removeSelectedItems() { + List selected = new ArrayList<>(selectedListView.getSelectionModel().getSelectedItems()); + for (T t : selected) { + if(!availableListView.getItems().contains(t)) { + availableListView.getItems().add(t); + selectedListView.getItems().remove(t); + } + } + Collections.sort(availableListView.getItems()); + } + + public List getSelectedItems() { + if(selectAll.isSelected()) { + List all = new ArrayList<>(availableListView.getItems()); + all.addAll(selectedListView.getItems()); + return all; + } else { + return selectedListView.getItems(); + } + } + + public boolean isAllSelected() { + return selectAll.isSelected(); + } +} diff --git a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java index f5f20dff..b66c470f 100644 --- a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java +++ b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java @@ -15,6 +15,7 @@ import ctbrec.Config; import ctbrec.Hmac; import ctbrec.Settings.DirectoryStructure; import ctbrec.StringUtil; +import ctbrec.recorder.Recorder; import ctbrec.sites.ConfigUI; import ctbrec.sites.Site; import ctbrec.ui.SiteUiFactory; @@ -73,9 +74,11 @@ public class SettingsTab extends Tab implements TabSelectionListener { private List sites; private Label restartLabel; private Accordion siteConfigAccordion = new Accordion(); + private Recorder recorder; - public SettingsTab(List sites) { + public SettingsTab(List sites, Recorder recorder) { this.sites = sites; + this.recorder = recorder; setText("Settings"); createGui(); setClosable(false); @@ -125,7 +128,7 @@ public class SettingsTab extends Tab implements TabSelectionListener { //right side rightSide.getChildren().add(siteConfigAccordion); - ActionSettingsPanel actions = new ActionSettingsPanel(this); + ActionSettingsPanel actions = new ActionSettingsPanel(this, recorder); rightSide.getChildren().add(actions); proxySettingsPane = new ProxySettingsPane(this); rightSide.getChildren().add(proxySettingsPane); diff --git a/common/src/main/java/ctbrec/AbstractModel.java b/common/src/main/java/ctbrec/AbstractModel.java index cd9803e7..53198b05 100644 --- a/common/src/main/java/ctbrec/AbstractModel.java +++ b/common/src/main/java/ctbrec/AbstractModel.java @@ -3,6 +3,7 @@ package ctbrec; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.concurrent.ExecutionException; import com.squareup.moshi.JsonReader; @@ -162,6 +163,13 @@ public abstract class AbstractModel implements Model { return true; } + @Override + public int compareTo(Model o) { + String thisName = Optional.ofNullable(getDisplayName()).orElse("").toLowerCase(); + String otherName = Optional.ofNullable(o).map(m -> m.getDisplayName()).orElse("").toLowerCase(); + return thisName.compareTo(otherName); + } + @Override public String toString() { return getName(); diff --git a/common/src/main/java/ctbrec/Model.java b/common/src/main/java/ctbrec/Model.java index 77127903..feb73817 100644 --- a/common/src/main/java/ctbrec/Model.java +++ b/common/src/main/java/ctbrec/Model.java @@ -12,7 +12,7 @@ import com.squareup.moshi.JsonWriter; import ctbrec.recorder.download.StreamSource; import ctbrec.sites.Site; -public interface Model { +public interface Model extends Comparable { public static enum State { ONLINE("online"), diff --git a/common/src/main/java/ctbrec/Settings.java b/common/src/main/java/ctbrec/Settings.java index bb19eb34..46998661 100644 --- a/common/src/main/java/ctbrec/Settings.java +++ b/common/src/main/java/ctbrec/Settings.java @@ -4,6 +4,8 @@ import java.io.File; import java.util.ArrayList; import java.util.List; +import ctbrec.event.EventHandlerConfiguration; + public class Settings { public enum ProxyType { @@ -56,7 +58,8 @@ public class Settings { public String cam4Password; public String lastDownloadDir = ""; - public List models = new ArrayList(); + public List models = new ArrayList<>(); + public List eventHandlers = new ArrayList<>(); public boolean determineResolution = false; public boolean requireAuthentication = false; public boolean chooseStreamQuality = false; diff --git a/common/src/main/java/ctbrec/event/Action.java b/common/src/main/java/ctbrec/event/Action.java index a71b99af..67ccf08e 100644 --- a/common/src/main/java/ctbrec/event/Action.java +++ b/common/src/main/java/ctbrec/event/Action.java @@ -2,6 +2,8 @@ package ctbrec.event; import java.util.function.Consumer; +import ctbrec.event.EventHandlerConfiguration.ActionConfiguration; + public abstract class Action implements Consumer { protected String name; @@ -13,4 +15,11 @@ public abstract class Action implements Consumer { public void setName(String name) { this.name = name; } + + public abstract void configure(ActionConfiguration config) throws Exception; + + @Override + public String toString() { + return name; + } } diff --git a/common/src/main/java/ctbrec/event/EventBusHolder.java b/common/src/main/java/ctbrec/event/EventBusHolder.java index e848c892..37d7b278 100644 --- a/common/src/main/java/ctbrec/event/EventBusHolder.java +++ b/common/src/main/java/ctbrec/event/EventBusHolder.java @@ -1,16 +1,37 @@ package ctbrec.event; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.Executors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.eventbus.AsyncEventBus; import com.google.common.eventbus.EventBus; public class EventBusHolder { - - public static final String EVENT = "event"; - public static final String OLD = "old"; - public static final String STATUS = "status"; - public static final String MODEL = "model"; + private static final transient Logger LOG = LoggerFactory.getLogger(EventBusHolder.class); + private static Map handlers = new HashMap<>(); public static final EventBus BUS = new AsyncEventBus(Executors.newSingleThreadExecutor()); + + public static void register(EventHandler handler) { + if(handlers.containsKey(handler.getId())) { + LOG.warn("EventHandler with ID {} is already registered", handler.getId()); + } else { + BUS.register(handler); + handlers.put(handler.getId(), handler); + LOG.debug("EventHandler with ID {} has been added", handler.getId()); + } + } + + public static void unregister(String id) { + EventHandler handler = handlers.get(id); + if(handler != null) { + BUS.unregister(handler); + handlers.remove(id); + LOG.debug("EventHandler with ID {} has been removed", id); + } + } } diff --git a/common/src/main/java/ctbrec/event/EventHandler.java b/common/src/main/java/ctbrec/event/EventHandler.java index 6621a2d8..4afd09a2 100644 --- a/common/src/main/java/ctbrec/event/EventHandler.java +++ b/common/src/main/java/ctbrec/event/EventHandler.java @@ -6,35 +6,132 @@ import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; -public class EventHandler { +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; - private List> predicates = new ArrayList<>(); - private List> actions; +import com.google.common.eventbus.Subscribe; + +import ctbrec.event.Event.Type; +import ctbrec.event.EventHandlerConfiguration.ActionConfiguration; +import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration; + +public class EventHandler { + private static final transient Logger LOG = LoggerFactory.getLogger(EventHandler.class); + + private List predicates = new ArrayList<>(); + private List actions; + private Type event; + private String id; + + public EventHandler(EventHandlerConfiguration config) { + id = config.getId(); + event = config.getEvent(); + actions = createActions(config); + predicates = createPredicates(config); + predicates.add(new EventTypePredicate(event)); + } + + public String getId() { + return id; + } @SafeVarargs - public EventHandler(Consumer action, Predicate... predicates) { + public EventHandler(Action action, EventPredicate... predicates) { this(Collections.singletonList(action), predicates); } @SafeVarargs - public EventHandler(List> actions, Predicate... predicates) { + public EventHandler(List actions, EventPredicate... predicates) { this.actions = actions; - for (Predicate predicate : predicates) { + for (EventPredicate predicate : predicates) { this.predicates.add(predicate); } } + @Subscribe public void reactToEvent(Event evt) { - boolean matches = true; - for (Predicate predicate : predicates) { - if(!predicate.test(evt)) { - matches = false; + try { + boolean matches = true; + for (Predicate predicate : predicates) { + if(!predicate.test(evt)) { + matches = false; + } } - } - if(matches) { - for (Consumer action : actions) { - action.accept(evt); + if(matches) { + for (Consumer action : actions) { + action.accept(evt); + } } + } catch(Exception e) { + LOG.error("Error while processing event", e); } } + + private List createPredicates(EventHandlerConfiguration config) { + List predicates = new ArrayList<>(config.getPredicates().size()); + for (PredicateConfiguration pc : config.getPredicates()) { + + try { + @SuppressWarnings("unchecked") + Class cls = (Class) Class.forName(pc.getType()); + if(cls == null) { + LOG.warn("Ignoring unknown action {}", cls); + continue; + } + EventPredicate predicate = cls.newInstance(); + predicate.configure(pc); + predicates.add(predicate); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { + LOG.warn("Error while creating action {} {}", pc.getType(), pc.getConfiguration(), e); + } + } + return predicates; + } + + private List createActions(EventHandlerConfiguration config) { + List actions = new ArrayList<>(config.getActions().size()); + for (ActionConfiguration ac : config.getActions()) { + try { + @SuppressWarnings("unchecked") + Class cls = (Class) Class.forName(ac.getType()); + if(cls == null) { + LOG.warn("Ignoring unknown action {}", cls); + continue; + } + Action action = cls.newInstance(); + action.configure(ac); + actions.add(action); + } catch (Exception e) { + LOG.warn("Error while creating action {} {}", ac.getType(), ac.getConfiguration(), e); + } + } + return actions; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + EventHandler other = (EventHandler) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } + + } diff --git a/common/src/main/java/ctbrec/event/EventHandlerConfiguration.java b/common/src/main/java/ctbrec/event/EventHandlerConfiguration.java index 8b2f3b15..f3d7df81 100644 --- a/common/src/main/java/ctbrec/event/EventHandlerConfiguration.java +++ b/common/src/main/java/ctbrec/event/EventHandlerConfiguration.java @@ -4,14 +4,30 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; + +import ctbrec.Model; public class EventHandlerConfiguration { + private String id; private String name; private Event.Type event; private List predicates = new ArrayList<>(); private List actions = new ArrayList<>(); + public EventHandlerConfiguration() { + id = UUID.randomUUID().toString(); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + public Event.Type getEvent() { return event; } @@ -44,10 +60,16 @@ public class EventHandlerConfiguration { this.actions = actions; } - public class PredicateConfiguration { + public static class PredicateConfiguration { + private String name; private String type; + private List models; private Map configuration = new HashMap<>(); + public void setName(String name) { + this.name = name; + } + public String getType() { return type; } @@ -64,9 +86,22 @@ public class EventHandlerConfiguration { this.configuration = configuration; } + public List getModels() { + return models; + } + + public void setModels(List models) { + this.models = models; + } + + @Override + public String toString() { + return name; + } } - public class ActionConfiguration { + public static class ActionConfiguration { + private String name; private String type; private Map configuration = new HashMap<>(); @@ -85,5 +120,44 @@ public class EventHandlerConfiguration { public void setConfiguration(Map configuration) { this.configuration = configuration; } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + } + + @Override + public String toString() { + return name + ", when:" + predicates + " do:" + actions + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + EventHandlerConfiguration other = (EventHandlerConfiguration) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; } } diff --git a/common/src/main/java/ctbrec/event/EventPredicate.java b/common/src/main/java/ctbrec/event/EventPredicate.java new file mode 100644 index 00000000..298ffb48 --- /dev/null +++ b/common/src/main/java/ctbrec/event/EventPredicate.java @@ -0,0 +1,10 @@ +package ctbrec.event; + +import java.util.function.Predicate; + +import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration; + +public abstract class EventPredicate implements Predicate { + + public abstract void configure(PredicateConfiguration pc); +} diff --git a/common/src/main/java/ctbrec/event/EventTypePredicate.java b/common/src/main/java/ctbrec/event/EventTypePredicate.java index 63bb3965..35135122 100644 --- a/common/src/main/java/ctbrec/event/EventTypePredicate.java +++ b/common/src/main/java/ctbrec/event/EventTypePredicate.java @@ -1,14 +1,16 @@ package ctbrec.event; -import java.util.function.Predicate; - import ctbrec.event.Event.Type; +import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration; -public class EventTypePredicate implements Predicate { +public class EventTypePredicate extends EventPredicate { private Type type; - private EventTypePredicate(Type type) { + public EventTypePredicate() { + } + + public EventTypePredicate(Type type) { this.type = type; } @@ -17,7 +19,8 @@ public class EventTypePredicate implements Predicate { return evt.getType() == type; } - public static EventTypePredicate of(Type type) { - return new EventTypePredicate(type); + @Override + public void configure(PredicateConfiguration pc) { + type = Type.valueOf((String) pc.getConfiguration().get("type")); } } diff --git a/common/src/main/java/ctbrec/event/ExecuteProgram.java b/common/src/main/java/ctbrec/event/ExecuteProgram.java new file mode 100644 index 00000000..27c08ca1 --- /dev/null +++ b/common/src/main/java/ctbrec/event/ExecuteProgram.java @@ -0,0 +1,53 @@ +package ctbrec.event; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ctbrec.OS; +import ctbrec.event.EventHandlerConfiguration.ActionConfiguration; +import ctbrec.io.StreamRedirectThread; + +public class ExecuteProgram extends Action { + + private static final transient Logger LOG = LoggerFactory.getLogger(ExecuteProgram.class); + + private String executable; + + public ExecuteProgram() {} + + public ExecuteProgram(String executable) { + this.executable = executable; + name = "execute program"; + } + + @Override + public void accept(Event evt) { + Runtime rt = Runtime.getRuntime(); + Process process = null; + try { + String[] args = {executable}; // TODO fill args array + process = rt.exec(args, OS.getEnvironment()); + + // create threads, which read stdout and stderr of the player process. these are needed, + // because otherwise the internal buffer for these streams fill up and block the process + Thread std = new Thread(new StreamRedirectThread(process.getInputStream(), System.out)); + std.setName("Player stdout pipe"); + std.setDaemon(true); + std.start(); + Thread err = new Thread(new StreamRedirectThread(process.getErrorStream(), System.err)); + err.setName("Player stderr pipe"); + err.setDaemon(true); + err.start(); + + process.waitFor(); + LOG.debug("{} finished", name); + } catch (Exception e) { + LOG.error("Error while processing {}", e); + } + } + + @Override + public void configure(ActionConfiguration config) { + executable = (String) config.getConfiguration().get("file"); + } +} diff --git a/common/src/main/java/ctbrec/event/ModelPredicate.java b/common/src/main/java/ctbrec/event/ModelPredicate.java index e23c902c..d573ab6f 100644 --- a/common/src/main/java/ctbrec/event/ModelPredicate.java +++ b/common/src/main/java/ctbrec/event/ModelPredicate.java @@ -1,30 +1,57 @@ package ctbrec.event; +import java.util.List; import java.util.Objects; import java.util.function.Predicate; import ctbrec.Model; +import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration; -public class ModelPredicate implements Predicate { +public class ModelPredicate extends EventPredicate { - private Model model; + private Predicate internal; - private ModelPredicate(Model model) { - this.model = model; + public ModelPredicate() {} + + public ModelPredicate(Model model) { + internal = createFor(model); + } + + public ModelPredicate(List models) { + configure(models); + } + + private void configure(List models) { + if(models.isEmpty()) { + throw new IllegalArgumentException("List has to contain at least one model"); + } + + Predicate predicate = createFor(models.get(0)); + for (int i = 1; i < models.size(); i++) { + predicate = predicate.or(createFor(models.get(i))); + } + internal = predicate; } @Override public boolean test(Event evt) { - if(evt instanceof AbstractModelEvent) { - AbstractModelEvent modelEvent = (AbstractModelEvent) evt; - Model other = modelEvent.getModel(); - return Objects.equals(model, other); - } else { - return false; - } + return internal.test(evt); } - public static ModelPredicate of(Model model) { - return new ModelPredicate(model); + private Predicate createFor(Model model) { + return evt -> { + if(evt instanceof AbstractModelEvent) { + AbstractModelEvent modelEvent = (AbstractModelEvent) evt; + Model other = modelEvent.getModel(); + return Objects.equals(model, other); + } else { + return false; + } + }; + } + + @Override + public void configure(PredicateConfiguration pc) { + configure(pc.getModels()); } } diff --git a/common/src/main/java/ctbrec/event/ModelStatePredicate.java b/common/src/main/java/ctbrec/event/ModelStatePredicate.java index 3dad100e..9df7b97a 100644 --- a/common/src/main/java/ctbrec/event/ModelStatePredicate.java +++ b/common/src/main/java/ctbrec/event/ModelStatePredicate.java @@ -1,20 +1,22 @@ package ctbrec.event; -import java.util.function.Predicate; - import ctbrec.Model; +import ctbrec.Model.State; +import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration; -public class ModelStatePredicate implements Predicate { +public class ModelStatePredicate extends EventPredicate { private Model.State state; - private ModelStatePredicate(Model.State state) { + public ModelStatePredicate() {} + + public ModelStatePredicate(Model.State state) { this.state = state; } @Override public boolean test(Event evt) { - if(evt instanceof AbstractModelEvent) { + if(evt instanceof ModelStateChangedEvent) { ModelStateChangedEvent modelEvent = (ModelStateChangedEvent) evt; Model.State newState = modelEvent.getNewState(); return newState == state; @@ -23,7 +25,8 @@ public class ModelStatePredicate implements Predicate { } } - public static ModelStatePredicate of(Model.State state) { - return new ModelStatePredicate(state); + @Override + public void configure(PredicateConfiguration pc) { + state = State.valueOf((String) pc.getConfiguration().get("state")); } } diff --git a/common/src/main/java/ctbrec/event/RecordingStateChangedEvent.java b/common/src/main/java/ctbrec/event/RecordingStateChangedEvent.java index f260552e..54c05e05 100644 --- a/common/src/main/java/ctbrec/event/RecordingStateChangedEvent.java +++ b/common/src/main/java/ctbrec/event/RecordingStateChangedEvent.java @@ -47,6 +47,10 @@ public class RecordingStateChangedEvent extends AbstractModelEvent { }; } + public State getState() { + return newState; + } + @Override public String toString() { return "RecordingStateChanged[" + newState + "," + model.getDisplayName() + "," + path + "]"; diff --git a/common/src/main/java/ctbrec/event/RecordingStatePredicate.java b/common/src/main/java/ctbrec/event/RecordingStatePredicate.java new file mode 100644 index 00000000..bad65223 --- /dev/null +++ b/common/src/main/java/ctbrec/event/RecordingStatePredicate.java @@ -0,0 +1,31 @@ +package ctbrec.event; + +import ctbrec.Recording; +import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration; + +public class RecordingStatePredicate extends EventPredicate { + + private Recording.State state; + + public RecordingStatePredicate() {} + + public RecordingStatePredicate(Recording.State state) { + this.state = state; + } + + @Override + public boolean test(Event evt) { + if(evt instanceof RecordingStateChangedEvent) { + RecordingStateChangedEvent event = (RecordingStateChangedEvent) evt; + Recording.State newState = event.getState(); + return newState == state; + } else { + return false; + } + } + + @Override + public void configure(PredicateConfiguration pc) { + state = Recording.State.valueOf((String) pc.getConfiguration().get("state")); + } +}