diff --git a/src/main/java/com/codedead/opal/controller/AboutWindowController.java b/src/main/java/com/codedead/opal/controller/AboutWindowController.java index 397c22a..42c9fba 100644 --- a/src/main/java/com/codedead/opal/controller/AboutWindowController.java +++ b/src/main/java/com/codedead/opal/controller/AboutWindowController.java @@ -64,7 +64,7 @@ public void setSettingsController(final SettingsController settingsController) { } /** - * Method that is invoked to initialize the FXML window + * Method that is invoked to initialize the FXML object */ @FXML private void initialize() { diff --git a/src/main/java/com/codedead/opal/controller/AudioController.java b/src/main/java/com/codedead/opal/controller/AudioController.java deleted file mode 100644 index 90ffeae..0000000 --- a/src/main/java/com/codedead/opal/controller/AudioController.java +++ /dev/null @@ -1,312 +0,0 @@ -package com.codedead.opal.controller; - -import com.codedead.opal.interfaces.IAudioTimer; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import javafx.scene.media.Media; -import javafx.scene.media.MediaPlayer; -import javafx.util.Duration; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; - -public final class AudioController { - - private Map mediaVolumes; - - private final Map mediaPlayers; - private final Timer timer; - private TimerTask timerTask; - - private boolean timerEnabled; - - private final IAudioTimer audioTimer; - private final ObjectMapper objectMapper; - private final Logger logger; - - /** - * Initialize a new AudioController - * - * @param audioTimer The {@link IAudioTimer} interface that can be used to call back certain timer functionalities - * @throws URISyntaxException When the URI syntax is incorrect - */ - public AudioController(final IAudioTimer audioTimer) throws URISyntaxException { - logger = LogManager.getLogger(AudioController.class); - - logger.info("Initializing new AudioController object"); - - mediaPlayers = new HashMap<>(); - mediaPlayers.put("rain", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/rain.mp3")).toURI().toString()))); - mediaPlayers.put("wind", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/wind.mp3")).toURI().toString()))); - mediaPlayers.put("thunder", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/thunder.mp3")).toURI().toString()))); - mediaPlayers.put("birds", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/birds.mp3")).toURI().toString()))); - mediaPlayers.put("river", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/river.mp3")).toURI().toString()))); - mediaPlayers.put("keyboard", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/typing.mp3")).toURI().toString()))); - mediaPlayers.put("telephone", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/telephone.mp3")).toURI().toString()))); - mediaPlayers.put("officeChatter", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/office.mp3")).toURI().toString()))); - mediaPlayers.put("traffic", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/traffic.mp3")).toURI().toString()))); - mediaPlayers.put("clock", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/clock.mp3")).toURI().toString()))); - mediaPlayers.put("fireplace", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/fireplace.mp3")).toURI().toString()))); - mediaPlayers.put("static", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/static.mp3")).toURI().toString()))); - mediaPlayers.put("fantasy", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/fantasy.mp3")).toURI().toString()))); - mediaPlayers.put("fan", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/fan.mp3")).toURI().toString()))); - mediaPlayers.put("cave", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/cave.mp3")).toURI().toString()))); - mediaPlayers.put("frogs", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/frogs.mp3")).toURI().toString()))); - mediaPlayers.put("zen", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/zen.mp3")).toURI().toString()))); - mediaPlayers.put("coffee", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/coffee.mp3")).toURI().toString()))); - mediaPlayers.put("zoo", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/zoo.mp3")).toURI().toString()))); - mediaPlayers.put("networking", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/networking.mp3")).toURI().toString()))); - mediaPlayers.put("tribal", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/tribal.mp3")).toURI().toString()))); - mediaPlayers.put("drumtribal", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/tribal2.mp3")).toURI().toString()))); - mediaPlayers.put("football", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/football.mp3")).toURI().toString()))); - mediaPlayers.put("sleepy", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/sleepy.mp3")).toURI().toString()))); - mediaPlayers.put("gong", new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource("/audio/gong.mp3")).toURI().toString()))); - - mediaVolumes = new HashMap<>(); - for (final Map.Entry entry : mediaPlayers.entrySet()) { - final MediaPlayer player = entry.getValue(); - player.setOnEndOfMedia(() -> player.seek(Duration.ZERO)); - mediaVolumes.put(entry.getKey(), 0.0); - } - - timer = new Timer(); - - this.audioTimer = audioTimer; - this.objectMapper = new ObjectMapper(); - } - - /** - * Play the {@link Media} object for a {@link MediaPlayer} object with the specified key - * - * @param key The key for which the {@link Media} object should be played - */ - public void playMedia(final String key) { - if (key == null) - throw new NullPointerException("Key cannot be null!"); - if (key.isEmpty()) - throw new IllegalArgumentException("Key cannot be empty!"); - - final MediaPlayer player = mediaPlayers.get(key); - - if (player == null) - throw new NullPointerException(String.format("MediaPlayer with key %s cannot be found!", key)); - - if (!player.getStatus().equals(MediaPlayer.Status.PLAYING)) { - logger.info("Playing Media for MediaPlayer with key {}", key); - player.play(); - } - } - - /** - * Stop playing the {@link Media} object for a {@link MediaPlayer} object with a specified key - * - * @param key The key for the {@link MediaPlayer} object for which the {@link Media} object should stop playing - */ - public void stopMedia(final String key) { - if (key == null) - throw new NullPointerException("Key cannot be null!"); - if (key.isEmpty()) - throw new IllegalArgumentException("Key cannot be empty!"); - - logger.info("Stopping Media for MediaPlayer with key {}", key); - - final MediaPlayer player = mediaPlayers.get(key); - - if (player == null) - throw new NullPointerException(String.format("MediaPlayer with key %s cannot be found!", key)); - - player.stop(); - } - - /** - * Set the volume for the {@link MediaPlayer} with the specified key - * - * @param key The key for the {@link MediaPlayer} object - * @param newVolume The new volume for the {@link MediaPlayer} with the specified key - */ - public void setPlayerVolume(final String key, final double newVolume) { - if (key == null) - throw new NullPointerException("Key cannot be null!"); - if (key.isEmpty()) - throw new IllegalArgumentException("Key cannot be empty!"); - if (newVolume < 0) - throw new IllegalArgumentException("Volume cannot be lower than 0!"); - if (newVolume > 1) - throw new IllegalArgumentException("Volume cannot be higher than 1!"); - - logger.debug("Setting volume for MediaPlayer with key {} to {}", key, newVolume); - - final MediaPlayer player = mediaPlayers.get(key); - if (player == null) - throw new NullPointerException(String.format("MediaPlayer with key %s cannot be found!", key)); - - player.setVolume(newVolume); - - mediaVolumes.replace(key, newVolume); - - if (newVolume == 0) { - stopMedia(key); - } else { - playMedia(key); - } - } - - /** - * Add a new {@link MediaPlayer} object - * - * @param key The key for the {@link MediaPlayer} object - * @param mediaPlayer The {@link MediaPlayer} object that should be added - */ - public void addPlayer(final String key, final MediaPlayer mediaPlayer) { - if (key == null) - throw new NullPointerException("Key cannot be null!"); - if (key.isEmpty()) - throw new IllegalArgumentException("Key cannot be empty!"); - if (mediaPlayer == null) - throw new NullPointerException("MediaPlayer cannot be null!"); - - logger.info("Adding MediaPlayer with key {}", key); - - if (mediaPlayers.containsKey(key)) - throw new IllegalArgumentException(String.format("MediaPlayer with key %s already exists!", key)); - - mediaPlayer.setOnEndOfMedia(() -> mediaPlayer.seek(Duration.ZERO)); - - mediaPlayers.put(key, mediaPlayer); - mediaVolumes.put(key, 0.0); - } - - /** - * Delete the entry for the {@link MediaPlayer} with the specified key - * - * @param key The key of the {@link MediaPlayer} that should be removed - */ - public void deletePlayer(final String key) { - if (key == null) - throw new NullPointerException("Key cannot be null!"); - if (key.isEmpty()) - throw new IllegalArgumentException("Key cannot be empty!"); - - logger.info("Deleting MediaPlayer with key {}", key); - - mediaPlayers.remove(key); - mediaVolumes.remove(key); - } - - /** - * Load a sound preset from disk - * - * @param path The full path where the sound preset is stored on disk - * @throws IOException When the file could not be read or the data inside the file could not be parsed - */ - public void loadSoundPreset(final String path) throws IOException { - if (path == null) - throw new NullPointerException("Path cannot be null!"); - if (path.isEmpty()) - throw new IllegalArgumentException("Path cannot be empty!"); - - logger.info("Loading sound preset from {}", path); - - final Path filePath = Path.of(path); - final String actual = Files.readString(filePath); - - if (actual == null || actual.isEmpty()) - throw new IllegalArgumentException("Sound preset cannot be null or empty!"); - - final TypeReference> typeRef = new TypeReference<>() { - }; - - mediaVolumes = objectMapper.readValue(actual, typeRef); - - for (final Map.Entry entry : mediaVolumes.entrySet()) { - setPlayerVolume(entry.getKey(), entry.getValue()); - } - } - - /** - * Save the volume settings to disk - * - * @param path The full path where the volume settings should be stored on disk - * @throws IOException When the media volumes could not be stored to disk - */ - public void saveSoundPreset(final String path) throws IOException { - if (path == null) - throw new NullPointerException("Path cannot be null!"); - if (path.isEmpty()) - throw new IllegalArgumentException("Path cannot be empty!"); - - logger.info("Saving sound preset to {}", path); - - objectMapper.writeValue(new File(path), mediaVolumes); - } - - /** - * Get the {@link Set} that contains the keys and volume values for all {@link MediaPlayer} objects - * - * @return The {@link Set} that contains the keys and volume values for all {@link MediaPlayer} objects - */ - public Set> getVolumes() { - return mediaVolumes.entrySet(); - } - - /** - * Cancel the {@link Timer} object - */ - public void cancelTimer() { - logger.info("Cancelling the Timer to stop all MediaPlayer objects"); - - timerEnabled = false; - - if (timerTask != null) { - timerTask.cancel(); - timer.purge(); - } - - if (audioTimer != null) { - audioTimer.cancelled(); - } - } - - /** - * Schedule the {@link Timer} object to cancel all {@link MediaPlayer} objects - * - * @param delay The delay in milliseconds before the {@link Timer} object executes its function - */ - public void scheduleTimer(final long delay) { - if (delay < 1) - throw new IllegalArgumentException("Delay cannot be smaller than 1"); - - logger.info("Scheduling the Timer to stop all MediaPlayer objects after {} millisecond(s)", delay); - - timerEnabled = true; - - if (timerTask != null) { - timerTask.cancel(); - timer.purge(); - } - - timerTask = new TimerTask() { - @Override - public void run() { - logger.info("Timer has fired"); - if (timerEnabled) { - for (final String key : mediaPlayers.keySet()) { - stopMedia(key); - } - if (audioTimer != null) { - audioTimer.fired(); - } - } - timerEnabled = false; - } - }; - - timer.schedule(timerTask, delay); - } -} diff --git a/src/main/java/com/codedead/opal/controller/MainWindowController.java b/src/main/java/com/codedead/opal/controller/MainWindowController.java index 7feb876..9abcf6e 100644 --- a/src/main/java/com/codedead/opal/controller/MainWindowController.java +++ b/src/main/java/com/codedead/opal/controller/MainWindowController.java @@ -1,15 +1,15 @@ package com.codedead.opal.controller; -import com.codedead.opal.domain.InvalidHttpResponseCodeException; -import com.codedead.opal.domain.OsCheck; -import com.codedead.opal.domain.PlatformUpdate; -import com.codedead.opal.domain.SoundPane; +import com.codedead.opal.domain.*; import com.codedead.opal.interfaces.IAudioTimer; import com.codedead.opal.interfaces.IRunnableHelper; import com.codedead.opal.utils.*; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import javafx.application.Platform; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; +import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.*; @@ -19,6 +19,7 @@ import javafx.scene.input.Dragboard; import javafx.scene.input.TransferMode; import javafx.scene.layout.GridPane; +import javafx.scene.media.MediaPlayer; import javafx.stage.FileChooser; import javafx.stage.Stage; import org.apache.logging.log4j.LogManager; @@ -26,13 +27,16 @@ import java.io.File; import java.io.IOException; -import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.*; import static com.codedead.opal.utils.SharedVariables.DEFAULT_LOCALE; public final class MainWindowController implements IAudioTimer { + @FXML + private GridPane grpControls; @FXML private SoundPane snpGong; @FXML @@ -113,27 +117,32 @@ public final class MainWindowController implements IAudioTimer { private MenuItem mniOpenSoundPreset; private final String platformName; - private final AudioController audioController; private final HelpUtils helpUtils; private SettingsController settingsController; private UpdateController updateController; private ResourceBundle translationBundle; + private final ObjectMapper objectMapper; + private final Timer timer; + private final IAudioTimer audioTimer; + private TimerTask timerTask; + private boolean timerEnabled; private final Logger logger; /** * Initialize a new MainWindowController - * - * @throws URISyntaxException When the URI could not be formed */ - public MainWindowController() throws URISyntaxException { + public MainWindowController() { logger = LogManager.getLogger(MainWindowController.class); logger.info("Initializing new MainWindowController object"); platformName = OsCheck.getOperatingSystemType().name(); - audioController = new AudioController(this); helpUtils = new HelpUtils(); + + this.timer = new Timer(); + this.audioTimer = this; + this.objectMapper = new ObjectMapper(); } /** @@ -270,6 +279,28 @@ private void checkForUpdates(final boolean showNoUpdates) { } } + /** + * Get all {@link SoundPane} objects from a {@link GridPane} object + * + * @param parent The {@link GridPane} object + * @return The {@link List} of {@link SoundPane} objects inside the given {@link GridPane} object + */ + private List getAllSoundPanes(final GridPane parent) { + if (parent == null) + throw new NullPointerException("GridPane cannot be null!"); + + final List elements = new ArrayList<>(); + for (final Node node : parent.getChildren()) { + if (node instanceof GridPane p) { + elements.addAll(getAllSoundPanes(p)); + } + if (node instanceof SoundPane s) { + elements.add(s); + } + } + return elements; + } + /** * Open a file on the local filesystem * @@ -315,7 +346,7 @@ public UpdateController getUpdateController() { } /** - * Method that is invoked to initialize the FXML window + * Method that is invoked to initialize the FXML object */ @FXML private void initialize() { @@ -334,366 +365,15 @@ private void initialize() { mniHelp.setGraphic(new ImageView(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/images/help.png"))))); mnuTimer.setGraphic(new ImageView(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/images/timer.png"))))); - snpRain.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpRain.setPlaying(newVolume != 0); - audioController.setPlayerVolume("rain", newVolume); - }); - snpRain.getBtnPlayPause().setOnAction(event -> { - if (snpRain.isPlaying()) { - audioController.stopMedia("rain"); - } else { - audioController.playMedia("rain"); - } - snpRain.setPlaying(!snpRain.isPlaying()); - }); - - snpWind.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpWind.setPlaying(newVolume != 0); - audioController.setPlayerVolume("wind", newVolume); - }); - snpWind.getBtnPlayPause().setOnAction(event -> { - if (snpWind.isPlaying()) { - audioController.stopMedia("wind"); - } else { - audioController.playMedia("wind"); - } - snpWind.setPlaying(!snpWind.isPlaying()); - }); - - snpThunder.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpThunder.setPlaying(newVolume != 0); - audioController.setPlayerVolume("thunder", newVolume); - }); - snpThunder.getBtnPlayPause().setOnAction(event -> { - if (snpThunder.isPlaying()) { - audioController.stopMedia("thunder"); - } else { - audioController.playMedia("thunder"); - } - snpThunder.setPlaying(!snpThunder.isPlaying()); - }); - - snpBird.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpBird.setPlaying(newVolume != 0); - audioController.setPlayerVolume("birds", newVolume); - }); - snpBird.getBtnPlayPause().setOnAction(event -> { - if (snpBird.isPlaying()) { - audioController.stopMedia("birds"); - } else { - audioController.playMedia("birds"); - } - snpBird.setPlaying(!snpBird.isPlaying()); - }); - - snpRiver.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpRiver.setPlaying(newVolume != 0); - audioController.setPlayerVolume("river", newVolume); - }); - snpRiver.getBtnPlayPause().setOnAction(event -> { - if (snpRiver.isPlaying()) { - audioController.stopMedia("river"); - } else { - audioController.playMedia("river"); - } - snpRiver.setPlaying(!snpRiver.isPlaying()); - }); - - snpTyping.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpTyping.setPlaying(newVolume != 0); - audioController.setPlayerVolume("keyboard", newVolume); - }); - snpTyping.getBtnPlayPause().setOnAction(event -> { - if (snpTyping.isPlaying()) { - audioController.stopMedia("keyboard"); - } else { - audioController.playMedia("keyboard"); - } - snpTyping.setPlaying(!snpTyping.isPlaying()); - }); - - snpTelephone.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpTelephone.setPlaying(newVolume != 0); - audioController.setPlayerVolume("telephone", newVolume); - }); - snpTelephone.getBtnPlayPause().setOnAction(event -> { - if (snpTelephone.isPlaying()) { - audioController.stopMedia("telephone"); - } else { - audioController.playMedia("telephone"); - } - snpTelephone.setPlaying(!snpTelephone.isPlaying()); - }); - - snpChatter.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpChatter.setPlaying(newVolume != 0); - audioController.setPlayerVolume("officeChatter", newVolume); - }); - snpChatter.getBtnPlayPause().setOnAction(event -> { - if (snpChatter.isPlaying()) { - audioController.stopMedia("officeChatter"); - } else { - audioController.playMedia("officeChatter"); - } - snpChatter.setPlaying(!snpChatter.isPlaying()); - }); - - snpTraffic.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpTraffic.setPlaying(newVolume != 0); - audioController.setPlayerVolume("traffic", newVolume); - }); - snpTraffic.getBtnPlayPause().setOnAction(event -> { - if (snpTraffic.isPlaying()) { - audioController.stopMedia("traffic"); - } else { - audioController.playMedia("traffic"); - } - snpTraffic.setPlaying(!snpTraffic.isPlaying()); - }); - - snpClock.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpClock.setPlaying(newVolume != 0); - audioController.setPlayerVolume("clock", newVolume); - }); - snpClock.getBtnPlayPause().setOnAction(event -> { - if (snpClock.isPlaying()) { - audioController.stopMedia("clock"); - } else { - audioController.playMedia("clock"); - } - snpClock.setPlaying(!snpClock.isPlaying()); - }); - - snpFireplace.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpFireplace.setPlaying(newVolume != 0); - audioController.setPlayerVolume("fireplace", newVolume); - }); - snpFireplace.getBtnPlayPause().setOnAction(event -> { - if (snpFireplace.isPlaying()) { - audioController.stopMedia("fireplace"); - } else { - audioController.playMedia("fireplace"); - } - snpFireplace.setPlaying(!snpFireplace.isPlaying()); - }); - - snpStatic.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpStatic.setPlaying(newVolume != 0); - audioController.setPlayerVolume("static", newVolume); - }); - snpStatic.getBtnPlayPause().setOnAction(event -> { - if (snpStatic.isPlaying()) { - audioController.stopMedia("static"); - } else { - audioController.playMedia("static"); - } - snpStatic.setPlaying(!snpStatic.isPlaying()); - }); - - snpFantasy.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpFantasy.setPlaying(newVolume != 0); - audioController.setPlayerVolume("fantasy", newVolume); - }); - snpFantasy.getBtnPlayPause().setOnAction(event -> { - if (snpFantasy.isPlaying()) { - audioController.stopMedia("fantasy"); - } else { - audioController.playMedia("fantasy"); - } - snpFantasy.setPlaying(!snpFantasy.isPlaying()); - }); - - snpFan.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpFan.setPlaying(newVolume != 0); - audioController.setPlayerVolume("fan", newVolume); - }); - snpFan.getBtnPlayPause().setOnAction(event -> { - if (snpFan.isPlaying()) { - audioController.stopMedia("fan"); - } else { - audioController.playMedia("fan"); - } - snpFan.setPlaying(!snpFan.isPlaying()); - }); - - snpCave.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpCave.setPlaying(newVolume != 0); - audioController.setPlayerVolume("cave", newVolume); - }); - snpCave.getBtnPlayPause().setOnAction(event -> { - if (snpCave.isPlaying()) { - audioController.stopMedia("cave"); - } else { - audioController.playMedia("cave"); - } - snpCave.setPlaying(!snpCave.isPlaying()); - }); - - snpFrogs.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpFrogs.setPlaying(newVolume != 0); - audioController.setPlayerVolume("frogs", newVolume); - }); - snpFrogs.getBtnPlayPause().setOnAction(event -> { - if (snpFrogs.isPlaying()) { - audioController.stopMedia("frogs"); - } else { - audioController.playMedia("frogs"); - } - snpFrogs.setPlaying(!snpFrogs.isPlaying()); - }); - - snpZen.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpZen.setPlaying(newVolume != 0); - audioController.setPlayerVolume("zen", newVolume); - }); - snpZen.getBtnPlayPause().setOnAction(event -> { - if (snpZen.isPlaying()) { - audioController.stopMedia("zen"); - } else { - audioController.playMedia("zen"); - } - snpZen.setPlaying(!snpZen.isPlaying()); - }); - - snpCoffee.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpCoffee.setPlaying(newVolume != 0); - audioController.setPlayerVolume("coffee", newVolume); - }); - snpCoffee.getBtnPlayPause().setOnAction(event -> { - if (snpCoffee.isPlaying()) { - audioController.stopMedia("coffee"); - } else { - audioController.playMedia("coffee"); - } - snpCoffee.setPlaying(!snpCoffee.isPlaying()); - }); - - snpZoo.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpZoo.setPlaying(newVolume != 0); - audioController.setPlayerVolume("zoo", newVolume); - }); - snpZoo.getBtnPlayPause().setOnAction(event -> { - if (snpZoo.isPlaying()) { - audioController.stopMedia("zoo"); - } else { - audioController.playMedia("zoo"); - } - snpZoo.setPlaying(!snpZoo.isPlaying()); - }); - - snpSleepy.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpSleepy.setPlaying(newVolume != 0); - audioController.setPlayerVolume("sleepy", newVolume); - }); - snpSleepy.getBtnPlayPause().setOnAction(event -> { - if (snpSleepy.isPlaying()) { - audioController.stopMedia("sleepy"); - } else { - audioController.playMedia("sleepy"); - } - snpSleepy.setPlaying(!snpSleepy.isPlaying()); - }); - - snpGong.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpGong.setPlaying(newVolume != 0); - audioController.setPlayerVolume("gong", newVolume); - }); - snpGong.getBtnPlayPause().setOnAction(event -> { - if (snpGong.isPlaying()) { - audioController.stopMedia("gong"); - } else { - audioController.playMedia("gong"); - } - snpGong.setPlaying(!snpGong.isPlaying()); - }); - - // Audiences - snpNetworkingEvent.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpNetworkingEvent.setPlaying(newVolume != 0); - audioController.setPlayerVolume("networking", newVolume); - }); - snpNetworkingEvent.getBtnPlayPause().setOnAction(event -> { - if (snpNetworkingEvent.isPlaying()) { - audioController.stopMedia("networking"); - } else { - audioController.playMedia("networking"); - } - snpNetworkingEvent.setPlaying(!snpNetworkingEvent.isPlaying()); - }); - - snpTribal.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpTribal.setPlaying(newVolume != 0); - audioController.setPlayerVolume("tribal", newVolume); - }); - snpTribal.getBtnPlayPause().setOnAction(event -> { - if (snpTribal.isPlaying()) { - audioController.stopMedia("tribal"); - } else { - audioController.playMedia("tribal"); - } - snpTribal.setPlaying(!snpTribal.isPlaying()); - }); - - snpDrumTribal.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpDrumTribal.setPlaying(newVolume != 0); - audioController.setPlayerVolume("drumtribal", newVolume); - }); - snpDrumTribal.getBtnPlayPause().setOnAction(event -> { - if (snpDrumTribal.isPlaying()) { - audioController.stopMedia("drumtribal"); - } else { - audioController.playMedia("drumtribal"); - } - snpDrumTribal.setPlaying(!snpDrumTribal.isPlaying()); - }); - - snpRugbyFootball.getSlider().valueProperty().addListener((observableValue, oldValue, newValue) -> { - final double newVolume = newValue.doubleValue() / 100; - snpRugbyFootball.setPlaying(newVolume != 0); - audioController.setPlayerVolume("football", newVolume); - }); - snpRugbyFootball.getBtnPlayPause().setOnAction(event -> { - if (snpRugbyFootball.isPlaying()) { - audioController.stopMedia("football"); - } else { - audioController.playMedia("football"); - } - snpRugbyFootball.setPlaying(!snpRugbyFootball.isPlaying()); - }); - mniTimerEnabled.setOnAction(e -> { if (mniTimerEnabled.isSelected()) { final Properties properties = settingsController.getProperties(); final long timerDelay = Long.parseLong(properties.getProperty("timerDelay", "3600000")); - audioController.scheduleTimer(timerDelay); + scheduleTimer(timerDelay); } else { - audioController.cancelTimer(); + cancelTimer(); } }); } @@ -719,43 +399,34 @@ private void openSoundPresetAction() { /** * Open a sound preset * - * @param filePath The absolute path of the sound preset file + * @param path The absolute path of the sound preset file */ - private void openSoundPreset(final String filePath) { + private void openSoundPreset(final String path) { + if (path == null) + throw new NullPointerException("Path cannot be null!"); + if (path.isEmpty()) + throw new IllegalArgumentException("Path cannot be empty!"); + + logger.info("Loading sound preset from {}", path); + try { - audioController.loadSoundPreset(filePath); - for (final Map.Entry entry : audioController.getVolumes()) { - switch (entry.getKey()) { - case "rain" -> snpRain.getSlider().setValue(entry.getValue() * 100); - case "wind" -> snpWind.getSlider().setValue(entry.getValue() * 100); - case "thunder" -> snpThunder.getSlider().setValue(entry.getValue() * 100); - case "birds" -> snpBird.getSlider().setValue(entry.getValue() * 100); - case "river" -> snpRiver.getSlider().setValue(entry.getValue() * 100); - case "keyboard" -> snpTyping.getSlider().setValue(entry.getValue() * 100); - case "telephone" -> snpTelephone.getSlider().setValue(entry.getValue() * 100); - case "officeChatter" -> snpChatter.getSlider().setValue(entry.getValue() * 100); - case "traffic" -> snpTraffic.getSlider().setValue(entry.getValue() * 100); - case "fireplace" -> snpFireplace.getSlider().setValue(entry.getValue() * 100); - case "static" -> snpStatic.getSlider().setValue(entry.getValue() * 100); - case "fantasy" -> snpFantasy.getSlider().setValue(entry.getValue() * 100); - case "fan" -> snpFan.getSlider().setValue(entry.getValue() * 100); - case "clock" -> snpClock.getSlider().setValue(entry.getValue() * 100); - case "cave" -> snpCave.getSlider().setValue(entry.getValue() * 100); - case "frogs" -> snpFrogs.getSlider().setValue(entry.getValue() * 100); - case "zen" -> snpZen.getSlider().setValue(entry.getValue() * 100); - case "coffee" -> snpCoffee.getSlider().setValue(entry.getValue() * 100); - case "zoo" -> snpZoo.getSlider().setValue(entry.getValue() * 100); - case "networking" -> snpNetworkingEvent.getSlider().setValue(entry.getValue() * 100); - case "tribal" -> snpTribal.getSlider().setValue(entry.getValue() * 100); - case "football" -> snpRugbyFootball.getSlider().setValue(entry.getValue() * 100); - case "sleepy" -> snpSleepy.getSlider().setValue(entry.getValue() * 100); - case "drumtribal" -> snpDrumTribal.getSlider().setValue(entry.getValue() * 100); - case "gong" -> snpGong.getSlider().setValue(entry.getValue() * 100); - default -> logger.info("Unknown key found: {}", entry.getKey()); - } + final Path filePath = Path.of(path); + final String actual = Files.readString(filePath); + + if (actual == null || actual.isEmpty()) + throw new IllegalArgumentException("Sound preset cannot be null or empty!"); + + final TypeReference> typeRef = new TypeReference<>() { + }; + + final Map mediaVolumes = objectMapper.readValue(actual, typeRef); + final List soundPanes = getAllSoundPanes(grpControls); + + for (final Map.Entry entry : mediaVolumes.entrySet()) { + soundPanes.stream().filter(e -> e.getMediaKey().equals(entry.getKey())).forEach(e -> e.getSlider().setValue(entry.getValue())); } } catch (final IOException ex) { - logger.error("Unable to open the sound preset from {}", filePath, ex); + logger.error("Unable to open the sound preset from {}", path, ex); FxUtils.showErrorAlert(translationBundle.getString("OpenSoundPresetError"), ex.getMessage(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); } } @@ -775,11 +446,17 @@ private void saveSoundPresetAction() { if (file != null) { String filePath = file.getAbsolutePath(); + if (!filePath.toLowerCase().contains(".json")) { + filePath += ".json"; + } + + final Map mediaVolumes = new HashMap<>(); + for (final SoundPane p : getAllSoundPanes(grpControls)) { + mediaVolumes.put(p.getMediaKey(), p.getSlider().getValue()); + } + try { - if (!filePath.toLowerCase().contains(".json")) { - filePath += ".json"; - } - audioController.saveSoundPreset(filePath); + objectMapper.writeValue(new File(filePath), mediaVolumes); } catch (final IOException ex) { logger.error("Unable to save the sound settings to {}", filePath, ex); FxUtils.showErrorAlert(translationBundle.getString("SaveSoundPresetError"), ex.getMessage(), getClass().getResourceAsStream(SharedVariables.ICON_URL)); @@ -796,31 +473,10 @@ private void saveSoundPresetAction() { private void resetAction() { logger.info("Resetting all audio sliders"); - snpRain.getSlider().setValue(0); - snpWind.getSlider().setValue(0); - snpBird.getSlider().setValue(0); - snpRiver.getSlider().setValue(0); - snpThunder.getSlider().setValue(0); - snpTyping.getSlider().setValue(0); - snpTelephone.getSlider().setValue(0); - snpChatter.getSlider().setValue(0); - snpTraffic.getSlider().setValue(0); - snpClock.getSlider().setValue(0); - snpFireplace.getSlider().setValue(0); - snpStatic.getSlider().setValue(0); - snpFantasy.getSlider().setValue(0); - snpFan.getSlider().setValue(0); - snpCave.getSlider().setValue(0); - snpFrogs.getSlider().setValue(0); - snpZen.getSlider().setValue(0); - snpCoffee.getSlider().setValue(0); - snpZoo.getSlider().setValue(0); - snpNetworkingEvent.getSlider().setValue(0); - snpTribal.getSlider().setValue(0); - snpRugbyFootball.getSlider().setValue(0); - snpSleepy.getSlider().setValue(0); - snpDrumTribal.getSlider().setValue(0); - snpGong.getSlider().setValue(0); + final List soundPanes = getAllSoundPanes(grpControls); + for (final SoundPane p : soundPanes) { + p.getSlider().setValue(0); + } } /** @@ -1016,11 +672,13 @@ private void updateAction() { } /** - * Method that is called when the {@link AudioController} object's {@link Timer} object has fired + * Method that is called when the {@link Timer} object has fired */ @Override public void fired() { - resetAction(); + for (final SoundPane p : getAllSoundPanes(grpControls)) { + p.pause(); + } mniTimerEnabled.setSelected(false); if (Boolean.parseBoolean(settingsController.getProperties().getProperty("timerApplicationShutdown", "false"))) { @@ -1029,7 +687,7 @@ public void fired() { } /** - * Method that is invoked when the {@link AudioController} object's {@link Timer} object has cancelled + * Method that is invoked when the object's {@link Timer} object has cancelled */ @Override public void cancelled() { @@ -1067,4 +725,54 @@ private void onDragDropped(final DragEvent dragEvent) { dragEvent.setDropCompleted(success); dragEvent.consume(); } + + /** + * Cancel the {@link Timer} object + */ + public void cancelTimer() { + logger.info("Cancelling the Timer to stop all MediaPlayer objects"); + + timerEnabled = false; + + if (timerTask != null) { + timerTask.cancel(); + timer.purge(); + } + + if (audioTimer != null) { + audioTimer.cancelled(); + } + } + + /** + * Schedule the {@link Timer} object to cancel all {@link MediaPlayer} objects + * + * @param delay The delay in milliseconds before the {@link Timer} object executes its function + */ + public void scheduleTimer(final long delay) { + if (delay < 1) + throw new IllegalArgumentException("Delay cannot be smaller than 1"); + + logger.info("Scheduling the Timer to stop all MediaPlayer objects after {} millisecond(s)", delay); + + timerEnabled = true; + + if (timerTask != null) { + timerTask.cancel(); + timer.purge(); + } + + timerTask = new TimerTask() { + @Override + public void run() { + logger.info("Timer has fired"); + if (timerEnabled) { + audioTimer.fired(); + } + timerEnabled = false; + } + }; + + timer.schedule(timerTask, delay); + } } diff --git a/src/main/java/com/codedead/opal/controller/SettingsWindowController.java b/src/main/java/com/codedead/opal/controller/SettingsWindowController.java index 3c69b48..5d6ecd6 100644 --- a/src/main/java/com/codedead/opal/controller/SettingsWindowController.java +++ b/src/main/java/com/codedead/opal/controller/SettingsWindowController.java @@ -54,7 +54,7 @@ public SettingsWindowController() { } /** - * Method that is invoked to initialize the FXML window + * Method that is invoked to initialize the FXML object */ @FXML private void initialize() { diff --git a/src/main/java/com/codedead/opal/controller/UpdateController.java b/src/main/java/com/codedead/opal/controller/UpdateController.java index 0ea8b67..4ba417b 100644 --- a/src/main/java/com/codedead/opal/controller/UpdateController.java +++ b/src/main/java/com/codedead/opal/controller/UpdateController.java @@ -89,25 +89,25 @@ public Optional checkForUpdates(final String currentPlatform, fi * @return 1 if v1 is larger than v2, -1 if v2 is larger than v1 and 0 if both are equal */ private int versionCompare(final String v1, final String v2) { - int vnum1 = 0; - int vnum2 = 0; + int vNum1 = 0; + int vNum2 = 0; for (int i = 0, j = 0; (i < v1.length() || j < v2.length()); ) { while (i < v1.length() && v1.charAt(i) != '.') { - vnum1 = vnum1 * 10 + (v1.charAt(i) - '0'); + vNum1 = vNum1 * 10 + (v1.charAt(i) - '0'); i++; } while (j < v2.length() && v2.charAt(j) != '.') { - vnum2 = vnum2 * 10 + (v2.charAt(j) - '0'); + vNum2 = vNum2 * 10 + (v2.charAt(j) - '0'); j++; } - if (vnum1 > vnum2) + if (vNum1 > vNum2) return 1; - if (vnum2 > vnum1) + if (vNum2 > vNum1) return -1; - vnum1 = vnum2 = 0; + vNum1 = vNum2 = 0; i++; j++; } @@ -131,9 +131,9 @@ public List getUpdates() throws IOException, InterruptedExceptio .build(); final HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); - if (response.statusCode() == 200) { + + if (response.statusCode() == 200) return Arrays.asList(objectMapper.readValue(response.body(), PlatformUpdate[].class)); - } throw new InvalidHttpResponseCodeException(String.format("Invalid HTTP response code (%s)", response.statusCode())); } diff --git a/src/main/java/com/codedead/opal/domain/SoundPane.java b/src/main/java/com/codedead/opal/domain/SoundPane.java index 672a954..b5263dc 100644 --- a/src/main/java/com/codedead/opal/domain/SoundPane.java +++ b/src/main/java/com/codedead/opal/domain/SoundPane.java @@ -1,5 +1,9 @@ package com.codedead.opal.domain; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.control.Button; @@ -8,11 +12,16 @@ import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; +import javafx.scene.media.Media; +import javafx.scene.media.MediaPlayer; +import javafx.util.Duration; import java.io.IOException; +import java.net.URISyntaxException; import java.net.URL; import java.util.Objects; +@SuppressWarnings("unused") public final class SoundPane extends GridPane { @FXML @@ -27,7 +36,11 @@ public final class SoundPane extends GridPane { private boolean mediaButton; @FXML private ImageView imgMediaButton; - private boolean playing; + @FXML + private final StringProperty mediaPath = new SimpleStringProperty(); + @FXML + private String mediaKey; + private MediaPlayer mediaPlayer; /** * Initialize a new SoundPane @@ -39,6 +52,45 @@ public SoundPane() throws IOException { fxmlLoader.setRoot(this); fxmlLoader.setController(this); fxmlLoader.load(); + + mediaPath.addListener(new ChangeListener<>() { + @Override + public void changed(ObservableValue observable, String oldValue, String newValue) { + if (newValue != null && !newValue.isEmpty()) { + try { + mediaPlayer = new MediaPlayer(new Media(Objects.requireNonNull(getClass().getResource(newValue)).toURI().toString())); + mediaPlayer.setOnEndOfMedia(() -> mediaPlayer.seek(Duration.ZERO)); + mediaPlayer.volumeProperty().bindBidirectional(sldVolume.valueProperty()); + mediaPlayer.statusProperty().addListener(new ChangeListener<>() { + @Override + public void changed(ObservableValue observable, MediaPlayer.Status oldValue, MediaPlayer.Status newValue) { + if (newValue == MediaPlayer.Status.PLAYING) { + imgMediaButton.setImage(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/images/pause.png")))); + } else { + imgMediaButton.setImage(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/images/play.png")))); + } + } + }); + } catch (final URISyntaxException e) { + throw new RuntimeException(e); + } + } + } + }); + } + + /** + * Method that is invoked to initialize the FXML object + */ + @FXML + private void initialize() { + sldVolume.valueProperty().addListener((observable, oldValue, newValue) -> { + if (newValue != null && newValue.doubleValue() == 0 && (oldValue != null && oldValue.doubleValue() != 0)) { + pause(); + } else if (newValue != null && newValue.doubleValue() > 0 && (oldValue != null && oldValue.doubleValue() == 0)) { + play(); + } + }); } /** @@ -89,26 +141,6 @@ public void setName(final String name) { lblName.setText(name); } - /** - * Get the volume - * - * @return The volumne - */ - @FXML - public double getVolume() { - return sldVolume.getValue(); - } - - /** - * Set the volume - * - * @param volume The volume - */ - @FXML - public void setVolume(final double volume) { - sldVolume.setValue(volume); - } - /** * Get the slider * @@ -131,7 +163,7 @@ public boolean isMediaButton() { /** * Set whether media buttons are enabled * - * @param mediaButton True if media buttons shoudl be enabled, otherwise false + * @param mediaButton True if media buttons should be enabled, otherwise false */ @FXML public void setMediaButton(final boolean mediaButton) { @@ -141,29 +173,78 @@ public void setMediaButton(final boolean mediaButton) { } /** - * Get the {@link Button} object to play or pause media + * Get the {@link javafx.scene.media.Media} object path + * + * @return The {@link javafx.scene.media.Media} object path + */ + @FXML + public String getMediaPath() { + return mediaPath.getValue(); + } + + /** + * Set the {@link javafx.scene.media.Media} object path + * + * @param mediaPath The {@link javafx.scene.media.Media} object path + */ + @FXML + public void setMediaPath(final String mediaPath) { + if (mediaPath == null) + throw new NullPointerException("Media path cannot be null!"); + if (mediaPath.isEmpty()) + throw new IllegalArgumentException("Media path cannot be empty!"); + + this.mediaPath.setValue(mediaPath); + } + + /** + * Get the media key * - * @return The {@link Button} object to play or pause media + * @return The media key */ - public Button getBtnPlayPause() { - return btnPlayPause; + @FXML + public String getMediaKey() { + return mediaKey; } /** - * Get whether the {@link javafx.scene.media.MediaPlayer} is playing or not + * Set the media key * - * @return True if the {@link javafx.scene.media.MediaPlayer} is playing, otherwise false + * @param mediaKey The media key + */ + @FXML + public void setMediaKey(final String mediaKey) { + if (mediaKey == null) + throw new NullPointerException("Media key cannot be null!"); + if (mediaKey.isEmpty()) + throw new IllegalArgumentException("Media key cannot be empty!"); + + this.mediaKey = mediaKey; + } + + /** + * Play the {@link Media} object + */ + public void play() { + this.mediaPlayer.play(); + } + + /** + * Pause the {@link Media} object */ - public boolean isPlaying() { - return playing; + public void pause() { + this.mediaPlayer.pause(); } - public void setPlaying(final boolean playing) { - if (playing && !this.playing) { - imgMediaButton.setImage(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/images/pause.png")))); - } else if (!playing && this.playing) { - imgMediaButton.setImage(new Image(Objects.requireNonNull(getClass().getResourceAsStream("/images/play.png")))); + /** + * Play or pause the {@link Media} object + */ + @FXML + private void playPause() { + if (this.mediaPlayer.getStatus() == MediaPlayer.Status.PLAYING) { + pause(); + } else { + play(); } - this.playing = playing; } } diff --git a/src/main/java/com/codedead/opal/interfaces/IAudioTimer.java b/src/main/java/com/codedead/opal/interfaces/IAudioTimer.java index 0defda1..7d3b409 100644 --- a/src/main/java/com/codedead/opal/interfaces/IAudioTimer.java +++ b/src/main/java/com/codedead/opal/interfaces/IAudioTimer.java @@ -1,6 +1,8 @@ package com.codedead.opal.interfaces; -public interface IAudioTimer { +import com.codedead.opal.controller.MainWindowController; + +public sealed interface IAudioTimer permits MainWindowController { void fired(); diff --git a/src/main/resources/controls/SoundPane.fxml b/src/main/resources/controls/SoundPane.fxml index 8431f74..71c2116 100644 --- a/src/main/resources/controls/SoundPane.fxml +++ b/src/main/resources/controls/SoundPane.fxml @@ -39,14 +39,14 @@ - + -