Vak's profile picture

Published by

published

Category: Web, HTML, Tech

19/12/2021

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(); }
}


0 Kudos

Comments

Displaying 0 of 0 comments ( View all | Add Comment )