Skip to content

Commit 5737de7

Browse files
author
extremq
committed
- Add click sfx.
- Add about and usage. - Refactoring.
1 parent cbbaab1 commit 5737de7

File tree

5 files changed

+173
-97
lines changed

5 files changed

+173
-97
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ This is a Pomodoro-type app made in Java using Swift UI.
55

66
## How to use
77
1. Click on "Start timer" and input how many minutes you want to focus.
8-
* If you provide an invalid value, it will be defaulted to 30 minutes.
8+
* If you provide an invalid value, it will be defaulted to 25 minutes.
99
2. To skip the session, click on "Stop timer".
1010
3. When the timer reaches 00:00, the break will begin.
1111
4. Click on "Start timer" to start the break.

res/click.wav

44 KB
Binary file not shown.

src/com/codebind/App.java

Lines changed: 113 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,19 @@
33
import javax.swing.*;
44
import java.awt.event.ActionEvent;
55
import java.awt.event.ActionListener;
6-
import javax.sound.sampled.*;
7-
import java.net.URL;
8-
import java.io.File;
9-
import java.io.FileInputStream;
10-
import java.io.InputStream;
116
import java.util.Timer;
127
import java.util.TimerTask;
138

149
public class App {
10+
private JMenuBar menubar;
1511
private JPanel panelMain;
1612
private JButton buttonStart;
1713
private JButton buttonStop;
1814
private JLabel labelMsg;
1915
private JLabel labelTimer;
2016
private JPanel panelButtons;
2117
private long focusMinutes;
22-
private static final JFrame frame = new JFrame("App");
18+
private JFrame frame;
2319

2420
enum State {
2521
BOOTED,
@@ -76,27 +72,6 @@ public boolean isTimerStopped() {
7672
}
7773
// END SETTERS AND GETTERS
7874

79-
// Transform the remaining time to a human-readable format.
80-
public String convertMillisToMinutes(long millis, long periodOfTime) {
81-
long selectedLength = periodOfTime * 60 * 1000;
82-
long remainingTime = selectedLength - millis;
83-
long millisToSeconds = (remainingTime / 1000) % 60;
84-
long millisToMinutes = remainingTime / (1000 * 60);
85-
86-
if (remainingTime <= 0) {
87-
return "00:00";
88-
}
89-
90-
return String.format("%02d", millisToMinutes) + ":" + String.format("%02d", millisToSeconds);
91-
}
92-
93-
private void startFocusPeriod() {
94-
setLabelMsg(Locale.FOCUS_MESSAGE);
95-
setEnableButtonStop(true);
96-
setEnableButtonStart(false);
97-
setTimerStopped(false);
98-
}
99-
10075
// Creates a dialogue that asks the user for the focus period.
10176
private void requestFocusTime() {
10277
String m = JOptionPane.showInputDialog(Locale.TIME_QUESTION_MESSAGE);
@@ -105,28 +80,44 @@ private void requestFocusTime() {
10580
try {
10681
convertedMinutes = Integer.parseInt(m);
10782
} catch (Exception exception) {
108-
convertedMinutes = 30;
83+
convertedMinutes = 25;
10984
}
11085

11186
if (convertedMinutes < 1 || convertedMinutes > 60) {
112-
convertedMinutes = 30;
87+
convertedMinutes = 25;
11388
}
11489
setFocusMinutes(convertedMinutes);
11590
}
11691

117-
private void endFocusPeriod() {
118-
setState(State.BREAK);
119-
120-
setLabelMsg(Locale.FOCUS_DONE_MESSAGE);
92+
// Edits the message and the timer labels.
93+
private void resetTimerAndMessage(String message) {
94+
setLabelMsg(message);
12195
setEnableButtonStart(true);
12296
setEnableButtonStop(false);
12397
setTimerStopped(false);
12498
setLabelTimer("00:00");
99+
}
125100

126-
flashWindow();
127-
playAlarmSound();
101+
public void finishSessionAndDisplayMessage(String message, JFrame frame) {
102+
flashWindow(frame);
103+
Utils.playAlarmSound();
128104

129-
JOptionPane.showMessageDialog(null, Locale.FOCUS_DONE_MESSAGE_ALERT);
105+
JOptionPane.showMessageDialog(null, message);
106+
}
107+
108+
private void startFocusPeriod() {
109+
setLabelMsg(Locale.FOCUS_MESSAGE);
110+
setEnableButtonStop(true);
111+
setEnableButtonStart(false);
112+
setTimerStopped(false);
113+
}
114+
115+
private void endFocusPeriod() {
116+
// Ended the focus, we need to take a break now.
117+
setState(State.BREAK);
118+
119+
resetTimerAndMessage(Locale.FOCUS_DONE_MESSAGE);
120+
finishSessionAndDisplayMessage(Locale.FOCUS_DONE_MESSAGE_ALERT, frame);
130121
}
131122

132123
private void startBreakPeriod() {
@@ -137,106 +128,133 @@ private void startBreakPeriod() {
137128
}
138129

139130
private void endBreakPeriod() {
131+
// Ended the break, we need to now.
140132
setState(State.FOCUS);
141133

142-
setLabelMsg(Locale.BREAK_DONE_MESSAGE);
143-
setEnableButtonStart(true);
144-
setEnableButtonStop(false);
145-
setTimerStopped(false);
146-
setLabelTimer("00:00");
147-
148-
flashWindow();
149-
playAlarmSound();
150-
151-
JOptionPane.showMessageDialog(null, Locale.BREAK_DONE_MESSAGE_ALERT);
134+
resetTimerAndMessage(Locale.BREAK_DONE_MESSAGE);
135+
finishSessionAndDisplayMessage(Locale.BREAK_DONE_MESSAGE_ALERT, frame);
152136
}
153137

154-
private static void flashWindow() {
138+
public static void flashWindow(JFrame frame) {
155139
frame.setAlwaysOnTop(true);
156140
frame.toFront();
157141
frame.requestFocus();
158142
frame.setAlwaysOnTop(false);
159143
}
160144

161-
private void playAlarmSound() {
162-
URL url = this.getClass().getClassLoader().getResource("alarm.wav");
163-
try {
164-
assert url != null;
165-
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
166-
Clip clip = AudioSystem.getClip();
145+
private void startTimerTask() {
146+
long startTime = System.currentTimeMillis();
147+
Timer timer = new Timer();
167148

168-
clip.open(audioIn);
169-
clip.start();
149+
// Pick whether we choose to subtract from session or break time.
150+
long sessionMinutes = 0;
151+
if (getState() == State.BOOTED || getState() == State.FOCUS) {
152+
sessionMinutes = getFocusMinutes();
170153
}
171-
catch (Exception e) {
172-
System.out.println(e.getMessage());
154+
else if (getState() == State.BREAK) {
155+
sessionMinutes = getBreakMinutes();
173156
}
157+
158+
long finalSessionMinutes = sessionMinutes;
159+
timer.scheduleAtFixedRate(new TimerTask() {
160+
@Override
161+
public void run() {
162+
long systemTime = System.currentTimeMillis();
163+
164+
// If the timer is below zero, or it has been stopped, cancel the updates.
165+
if (systemTime - startTime > finalSessionMinutes * 60 * 1000 || isTimerStopped()) {
166+
this.cancel();
167+
if (getState() == State.BOOTED || getState() == State.FOCUS) {
168+
endFocusPeriod();
169+
}
170+
else if (getState() == State.BREAK) {
171+
endBreakPeriod();
172+
}
173+
}
174+
// Update the timer with the new remaining time.
175+
else {
176+
setLabelTimer(Utils.convertRemainingMillisToMinutes(systemTime - startTime, getFocusMinutes()));
177+
}
178+
}
179+
}, 0, 100);
180+
}
181+
182+
private void initFrame(JFrame frame) {
183+
frame.toFront();
184+
frame.setContentPane(this.panelMain);
185+
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
186+
frame.pack();
187+
frame.setVisible(true);
188+
frame.setResizable(false);
189+
frame.setTitle("Pomodoro app");
190+
frame.setLocationRelativeTo(null);
191+
frame.setSize(350, 250);
192+
}
193+
194+
private void initMenuBar(JMenuBar menubar, JFrame frame) {
195+
JMenuItem aboutItem = new JMenuItem("About");
196+
JMenuItem usageItem = new JMenuItem("Usage");
197+
JMenu helpMenu = new JMenu("Help");
198+
199+
aboutItem.addActionListener(new ActionListener() {
200+
@Override
201+
public void actionPerformed(ActionEvent e) {
202+
JOptionPane.showMessageDialog(null, Locale.ABOUT_MESSAGE);
203+
}
204+
});
205+
206+
usageItem.addActionListener(new ActionListener() {
207+
@Override
208+
public void actionPerformed(ActionEvent e) {
209+
JOptionPane.showMessageDialog(null, Locale.USAGE_MESSAGE);
210+
}
211+
});
212+
213+
helpMenu.add(aboutItem);
214+
helpMenu.add(usageItem);
215+
menubar.add(helpMenu);
216+
frame.setJMenuBar(menubar);
174217
}
175218

176219
public App() {
220+
this.frame = new JFrame();
221+
initFrame(this.frame);
222+
223+
this.menubar = new JMenuBar();
224+
initMenuBar(this.menubar, this.frame);
225+
177226
// Start button logic
178227
buttonStart.addActionListener(new ActionListener() {
179228
@Override
180229
public void actionPerformed(ActionEvent e) {
230+
Utils.playClickSound();
231+
181232
if (getState() == State.FOCUS || getState() == State.BOOTED) {
182233
// Only request the focus time the first time.
183234
if (getState() == State.BOOTED)
184235
requestFocusTime();
185236
startFocusPeriod();
186237

187-
long startTime = System.currentTimeMillis();
188-
Timer timer = new Timer();
189-
timer.scheduleAtFixedRate(new TimerTask() {
190-
@Override
191-
public void run() {
192-
long systemTime = System.currentTimeMillis();
193-
if (systemTime - startTime > getFocusMinutes() * 60 * 1000 || isTimerStopped()) {
194-
this.cancel();
195-
endFocusPeriod();
196-
} else {
197-
setLabelTimer(convertMillisToMinutes(systemTime - startTime, getFocusMinutes()));
198-
}
199-
}
200-
}, 0, 100);
238+
startTimerTask();
201239
}
202240
else if (getState() == State.BREAK) {
203241
startBreakPeriod();
204242

205-
long startTime = System.currentTimeMillis();
206-
Timer timer = new Timer();
207-
timer.scheduleAtFixedRate(new TimerTask() {
208-
@Override
209-
public void run() {
210-
long systemTime = System.currentTimeMillis();
211-
if (systemTime - startTime > getBreakMinutes() * 60 * 1000 || isTimerStopped()) {
212-
this.cancel();
213-
endBreakPeriod();
214-
} else {
215-
setLabelTimer(convertMillisToMinutes(systemTime - startTime, getBreakMinutes()));
216-
}
217-
}
218-
}, 0, 100);
243+
startTimerTask();
219244
}
220245
}
221246
});
222247
buttonStop.addActionListener(new ActionListener() {
223248
@Override
224249
public void actionPerformed(ActionEvent e) {
250+
Utils.playClickSound();
251+
225252
setTimerStopped(true);
226253
}
227254
});
228255
}
229256

230257
public static void main(String[] args) {
231-
// Init settings
232-
frame.toFront();
233-
frame.setContentPane(new App().panelMain);
234-
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
235-
frame.pack();
236-
frame.setVisible(true);
237-
frame.setResizable(false);
238-
frame.setTitle("Pomodoro app");
239-
frame.setLocationRelativeTo(null);
240-
frame.setSize(350, 225);
258+
new App();
241259
}
242260
}

src/com/codebind/Locale.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,22 @@ public class Locale {
44
public static final String FOCUS_MESSAGE = "It's time to focus.";
55
public static final String BREAK_MESSAGE = "Take a break!";
66
public static final String TIME_QUESTION_MESSAGE = "How much time do you want to focus (in minutes, 1-60)?" +
7-
"\n" + "The default is 30 minutes.";
7+
"\n" + "The default is 25 minutes.";
88
public static final String FOCUS_DONE_MESSAGE_ALERT = "Time is up! Take a break.";
99
public static final String BREAK_DONE_MESSAGE_ALERT = "Time is up! Let's focus!";
1010
public static final String FOCUS_DONE_MESSAGE = "Let's take a break.";
1111
public static final String BREAK_DONE_MESSAGE = "Let's focus.";
1212
public static final String GREET_MESSAGE = "Nice to see you!";
13+
public static final String ABOUT_MESSAGE = "This application was made by Extremq as an exercise and as a necessity." +
14+
"\n\n" + "The source code is available at https://github.com/extremq/pomodoro" +
15+
"\n" + "and is available freely to anyone." +
16+
"\n\n" + "© Extremq – 2022";
17+
public static final String USAGE_MESSAGE = "The Pomodoro Technique involves breaking an activity" +
18+
"\n" + "into 25 minute intervals. This app allows you to manage" +
19+
"\n" + "your activities in a simple and distraction-free way." +
20+
"\n\n" + "Click on Start Timer to start your first session. You will" +
21+
"\n" + "be asked how much time you want to focus. You can skip the" +
22+
"\n" + "session by pressing the Stop Timer button. When the time" +
23+
"\n" + "runs out, you will hear an alarm and the window will be" +
24+
"\n" + "back in focus.";
1325
}

src/com/codebind/Utils.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.codebind;
2+
3+
import javax.sound.sampled.AudioInputStream;
4+
import javax.sound.sampled.AudioSystem;
5+
import javax.sound.sampled.Clip;
6+
import java.net.URL;
7+
8+
public class Utils {
9+
// Transform the remaining time to a human-readable format.
10+
public static String convertRemainingMillisToMinutes(long millis, long periodOfTime) {
11+
long selectedLength = periodOfTime * 60 * 1000;
12+
long remainingTime = selectedLength - millis;
13+
long millisToSeconds = (remainingTime / 1000) % 60;
14+
long millisToMinutes = remainingTime / (1000 * 60);
15+
16+
if (remainingTime <= 0) {
17+
return "00:00";
18+
}
19+
20+
return String.format("%02d", millisToMinutes) + ":" + String.format("%02d", millisToSeconds);
21+
}
22+
23+
public static void playAlarmSound() {
24+
playSound("alarm.wav");
25+
}
26+
27+
public static void playClickSound() {
28+
playSound("click.wav");
29+
}
30+
31+
public static void playSound(String fileName) {
32+
URL url = Utils.class.getClassLoader().getResource(fileName);
33+
try {
34+
assert url != null;
35+
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
36+
Clip clip = AudioSystem.getClip();
37+
38+
clip.open(audioIn);
39+
clip.start();
40+
}
41+
catch (Exception e) {
42+
System.out.println(e.getMessage());
43+
}
44+
}
45+
46+
}

0 commit comments

Comments
 (0)