Spent today trying to wind down and testing out how much I actually learned from my Java course this semester. Decided to re-make something I made in python a while ago which plays atmospheric music for 30 mins, followed by a 5 minute break, and this repeats indefinitely.
The Python version I wrote was pretty terrible, something I cobbled together in 20 minutes with no UI and no ability to pause or see how much revision time you had left, so with this Java version I wanted to create a proper GUI and make it a little less janky. I was able to follow a YouTube tutorial for the GUI creation and folded in some code I found on how to create windows notifications. It's not finished at the moment, the play/pause button is a little weird if you click it a lot in a short amount of time and I still haven't figured out how to successfully compile it into a .jar file, but for the most part I'm happy with how it works. After I get it working properly I'll try and create a dark mode for the UI and maybe change it so that it randomises which song it plays after finishing instead of just repeating.
Source code for anyone interested:
Application.java
package com.example.revision_timer;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.Slider;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import java.awt.*;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
public class Controller implements Initializable {
// Creates FXML variables for all elements in the UI that need to be referenced.
@FXML
private Label songText;
@FXML
private Slider volume;
@FXML
private ProgressBar progressBar;
private File directory;
private ArrayList<File> songs;
private MediaPlayer mediaPlayer;
private Timer revisionTimer;
private final int reviDuration = 1800;
private final int restDuration = 300;
protected boolean running;
private boolean revision = false;
private int duration;
private double current = 0;
/**
* Initialised at the beginning of the program, initialises variables.
* @param url The location on disk used to work from when importing.
* @param resourceBundle N/A, contains null.
*/
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
songs = new ArrayList<>();
directory = new File("src/main/resources/Music");
File[] files = directory.listFiles();
if (files != null){
Collections.addAll(songs, files);
}
switchTimers();
//Creates a listener for the volume slider being changed.
volume.valueProperty().addListener((observableValue, number, t1) -> mediaPlayer.setVolume(volume.getValue() * 0.01));
}
//Randomly selects a new song from the library and plays it.
public void startNewSong(){
int amountSongs = Objects.requireNonNull(directory.listFiles()).length;
Random ran = new Random();
int songNumber = ran.nextInt(amountSongs);
Media media = new Media(songs.get(songNumber).toURI().toString());
mediaPlayer = new MediaPlayer(media);
mediaPlayer.setCycleCount(MediaPlayer.INDEFINITE);
String songName = songs.get(songNumber).getName();
Platform.runLater(() -> songText.setText(songName));
playPauseMedia();
}
public void playPauseMedia() {
if (running) {
mediaPlayer.pause();
running = false;
revisionTimer.cancel();
} else {
mediaPlayer.play();
beginTimer();
}
}
public void beginTimer(){
revisionTimer = new Timer();
mediaPlayer.setVolume(volume.getValue()*0.01);
TimerTask task = new TimerTask() {
public void run() {
running = true;
if (revision) {
current = mediaPlayer.getCurrentTime().toSeconds();
duration = reviDuration;
} else {
current += 1;
duration = restDuration;
}
progressBar.setProgress(current / duration);
if (current / duration > 1) {
revisionTimer.cancel();
switchTimers();
}
}
};
revisionTimer.scheduleAtFixedRate(task,1000,1000);
}
public void switchTimers(){
if (revision){
current = 0;
revision = false;
mediaPlayer.stop();
displayNotification();
beginTimer();
} else{
running = false;
revision = true;
startNewSong();
displayNotification();
}
}
public void displayNotification(){
if (SystemTray.isSupported()) {
try{
displayTray();
}catch(AWTException | MalformedURLException ignored){}
} else {
System.err.println("System tray not supported!");
}
}
public void displayTray() throws AWTException, MalformedURLException {
//Obtain only one instance of the SystemTray object
SystemTray tray = SystemTray.getSystemTray();
//If the icon is a file
Image image = Toolkit.getDefaultToolkit().createImage("src/main/resources/com/example/revision_timer/icon.png");
//Alternative (if the icon is on the classpath):
//Image image = Toolkit.getDefaultToolkit().createImage(getClass().getResource("icon.png"));
TrayIcon trayIcon = new TrayIcon(image, "Revision Timer");
//Let the system resize the image if needed
trayIcon.setImageAutoSize(true);
//Set tooltip text for the tray icon
trayIcon.setToolTip("Revision Timer");
tray.add(trayIcon);
if (revision) {
trayIcon.displayMessage("Get Revising", "30 minutes starting now", TrayIcon.MessageType.INFO);
} else {
trayIcon.displayMessage("Get Relaxing", "5 minutes starting now", TrayIcon.MessageType.INFO);
}
}
}
GUI.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.Label?>
<AnchorPane maxHeight="122.0" maxWidth="277.0" minHeight="122.0" minWidth="277.0" prefHeight="122.0" prefWidth="277.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.revision_timer.Controller">
<children>
<Button id="playPause" fx:id="playPause" layoutX="129.0" layoutY="14.0" mnemonicParsing="false" onAction="#playPauseMedia" prefHeight="78.0" prefWidth="105.0" text="Play/Pause"/>
<Label id="songText" fx:id="songText" layoutX="15.0" layoutY="14.0" prefHeight="78.0" prefWidth="106.0" style="-fx-background-color: #EAEAEA;" />
<ProgressBar id="progressBar" fx:id="progressBar" layoutX="14.0" layoutY="102.0" prefHeight="10.0" prefWidth="250.0" progress="0.0" />
<Slider id="volume" fx:id="volume" layoutX="248.0" layoutY="12.0" orientation="VERTICAL" prefHeight="78.0" prefWidth="14.0" value="40.0" />
</children>
</AnchorPane>
Application.java
package com.example.revision_timer;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.awt.*;
import java.awt.TrayIcon.MessageType;
import java.io.IOException;
public class Application extends javafx.application.Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(Application.class.getResource("GUI.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 277.0, 122.0);
stage.setTitle("Revision Timer");
stage.setScene(scene);
stage.show();
stage.setResizable(false);
stage.setOnCloseRequest(e -> {
Platform.exit();
System.exit(0);
});
}
public static void main(String[] args) { launch(); }
}
Comments
Displaying 0 of 0 comments ( View all | Add Comment )