1 package de.japrost.amaot;
2
3 import java.awt.AWTException;
4 import java.awt.Color;
5 import java.awt.Component;
6 import java.awt.Dimension;
7 import java.awt.Font;
8 import java.awt.Graphics2D;
9 import java.awt.Image;
10 import java.awt.SystemTray;
11 import java.awt.TrayIcon;
12 import java.awt.event.ActionEvent;
13 import java.awt.event.ActionListener;
14 import java.awt.event.InputEvent;
15 import java.awt.event.MouseAdapter;
16 import java.awt.event.MouseEvent;
17 import java.awt.image.BufferedImage;
18 import java.net.URL;
19
20 import javax.swing.ImageIcon;
21 import javax.swing.JFrame;
22 import javax.swing.JLabel;
23 import javax.swing.JMenuItem;
24 import javax.swing.JPopupMenu;
25 import javax.swing.JTextArea;
26 import javax.swing.JWindow;
27 import javax.swing.SwingWorker;
28
29
30
31
32 public class Amaot implements ActionListener {
33 private JWindow mainWindow;
34 private JLabel mainLabel;
35 private JPopupMenu popup;
36 private JFrame aboutFrame;
37 private CountdownTask countdownTask = new CountdownTask();
38 private long duration = 15 * 60 * 1000;
39 private long initialDuration = duration;
40 private boolean started = false;
41 private boolean autoStart = false;
42 private int fontSizeList[] = { 16, 24, 36, 12 };
43 private int fontSizeIndex = 1;
44 private String timeFormat = "%01d min %02d sec";
45 private TrayIcon trayIcon;
46 private String imageIconName = "/Sanduhr.png";
47 private boolean showImage = true;
48
49 public static void main(String[] args) {
50 final Amaot amaot = new Amaot();
51 if (args.length > 0) {
52 amaot.duration = (long) (Float.parseFloat(args[0]) * 1000 * 60);
53 amaot.initialDuration = amaot.duration;
54 amaot.autoStart = true;
55 }
56 if (args.length > 1) {
57 amaot.timeFormat = "%01d : %02d ";
58 }
59 javax.swing.SwingUtilities.invokeLater(new Runnable() {
60 public void run() {
61 amaot.createAndShowGUI();
62 }
63 });
64 }
65
66 private void createAndShowGUI() {
67 mainWindow = new JWindow();
68 mainWindow.setAlwaysOnTop(true);
69 mainLabel = new JLabel(" AMAOT ");
70 mainLabel.setFont(new Font("sansserif", Font.BOLD, fontSizeList[fontSizeIndex]));
71 mainWindow.getContentPane().add(mainLabel);
72 popup = new JPopupMenu();
73 JMenuItem menuItem;
74 String[] menuEntries = { "Start", "Stop", "Reset", "+10", "+5", "+1", "-1", "-5", "-10", "Size", "Icon",
75 "About", "Close Menu", "Exit" };
76 for (String menuEntry : menuEntries) {
77 menuItem = new JMenuItem(menuEntry);
78 menuItem.addActionListener(this);
79 popup.add(menuItem);
80 }
81 MouseAdapter popupListener = new PopupListener();
82 mainWindow.addMouseListener(popupListener);
83 mainWindow.addMouseMotionListener(popupListener);
84 if (SystemTray.isSupported()) {
85 createSystemTray();
86 }
87 createAboutFrame();
88 showTimeOnDisplay(duration);
89 changeColor(Color.WHITE);
90 mainWindow.pack();
91 mainWindow.setVisible(true);
92 if (autoStart) {
93 started = true;
94 (countdownTask = new CountdownTask()).execute();
95 }
96 }
97
98 private void createSystemTray() {
99 SystemTray tray = SystemTray.getSystemTray();
100 Dimension trayIconSize = tray.getTrayIconSize();
101 Image myImage;
102 URL imageURL = Amaot.class.getResource(imageIconName);
103 if (showImage && imageURL != null) {
104 myImage = new ImageIcon(imageURL).getImage();
105 } else {
106 myImage = new BufferedImage(trayIconSize.width, trayIconSize.height, BufferedImage.TYPE_BYTE_INDEXED);
107 }
108 trayIcon = new TrayIcon(myImage,
109 " Sinnspruch des Tages:\n Dir bleibt stets immer etwas Zeit\n für Datenschutz und Sicherheit! ");
110 trayIcon.setImageAutoSize(true);
111 trayIcon.addMouseListener(new SystemTrayMouseAdapter());
112 try {
113 tray.add(trayIcon);
114 } catch (final AWTException e) {
115
116 }
117 }
118
119 private void createAboutFrame() {
120 aboutFrame = new JFrame("About: AMAOT");
121 JTextArea aboutText = new JTextArea(4, 30);
122 aboutText.append("AMAOT: A Michrochronometer Always On Top \n\nThis is just a small countdown timer. ");
123 aboutText.append("Use it if you like.\n But be aware of ");
124 aboutText.append("the licensing conditions:\n If you use ");
125 aboutText.append("it more than once a day, you have to ");
126 aboutText.append("consider \n data protection and ");
127 aboutText.append("IT-security for some seconds!");
128 aboutText.setEditable(false);
129 aboutFrame.add(aboutText);
130 aboutFrame.pack();
131 }
132
133 public void actionPerformed(ActionEvent e) {
134 JMenuItem source = (JMenuItem) (e.getSource());
135 String eventText = source.getText();
136 boolean keepMenuVisible = false;
137 if ("Exit".equals(eventText)) {
138 System.exit(0);
139 } else if ("About".equals(eventText)) {
140
141 aboutFrame.setLocation(source.getX(), source.getY());
142 aboutFrame.setVisible(true);
143
144
145 } else if ("Start".equals(eventText)) {
146 startCountdown();
147 } else if ("Reset".equals(eventText)) {
148 resetCountdown();
149 } else if ("Icon".equals(eventText)) {
150 showImage = !showImage;
151 changeColor(mainWindow.getContentPane().getBackground());
152 } else if ("Stop".equals(eventText)) {
153 stopCountdown();
154 } else if ("Size".equals(eventText)) {
155 adjustFontSize();
156 keepMenuVisible = true;
157 } else if (eventText.startsWith("+") || eventText.startsWith("-")) {
158 adjustTime(eventText);
159 keepMenuVisible = true;
160 }
161 popup.setVisible(keepMenuVisible);
162 }
163
164 private void startCountdown() {
165 if (!started) {
166 started = true;
167 (countdownTask = new CountdownTask()).execute();
168 }
169 }
170
171 private void resetCountdown() {
172 stopCountdown();
173 duration = initialDuration;
174 showTimeOnDisplay(duration);
175 }
176
177 private void stopCountdown() {
178 countdownTask.cancel(true);
179 changeColor(Color.WHITE);
180 started = false;
181 }
182
183 private void adjustFontSize() {
184 fontSizeIndex = fontSizeIndex + 1;
185 if (fontSizeIndex >= fontSizeList.length) {
186 fontSizeIndex = 0;
187 }
188 mainLabel.setFont(new Font("sansserif", Font.BOLD, fontSizeList[fontSizeIndex]));
189 mainWindow.pack();
190 }
191
192 private void adjustTime(String minutes) {
193 if (minutes.startsWith("+")) {
194 minutes = minutes.substring(1);
195 }
196 int addMinutes = Integer.parseInt(minutes);
197 long newDuration = duration + addMinutes * 60 * 1000;
198 if (newDuration > 1000) {
199 if (addMinutes < 10) {
200 duration = newDuration;
201 } else {
202 duration = zeroSeconds(newDuration);
203 }
204 }
205 countdownTask.cancel(true);
206 showTimeOnDisplay(duration);
207 if (started) {
208 (countdownTask = new CountdownTask()).execute();
209 } else {
210 changeColor(Color.WHITE);
211 }
212 }
213
214 private long zeroSeconds(long millis) {
215 long min = millis / (60 * 1000);
216 long sec = (millis - (min * 60 * 1000)) / 1000;
217 if ((sec >= 29)) {
218 min = min + 1;
219 }
220 return (min * 60 * 1000);
221 }
222
223 private void changeColor(Color color) {
224 mainWindow.getContentPane().setBackground(color);
225 URL imageURL = Amaot.class.getResource(imageIconName);
226 if (showImage && imageURL != null) {
227 Image myImage = new ImageIcon(Amaot.class.getResource(imageIconName)).getImage();
228 trayIcon.setImage(myImage);
229 } else {
230 Dimension trayIconSize = trayIcon.getSize();
231 BufferedImage myImage = new BufferedImage(trayIconSize.width, trayIconSize.height,
232 BufferedImage.TYPE_BYTE_INDEXED);
233 Graphics2D graphics2d = myImage.createGraphics();
234 graphics2d.setColor(color);
235 graphics2d.fillRect(0, 0, myImage.getWidth(), myImage.getHeight());
236 trayIcon.setImage(myImage);
237 }
238 }
239
240 private void showTimeOnDisplay(long t) {
241 long min = t / (60 * 1000);
242 long sec = (t - (min * 60 * 1000)) / 1000;
243 mainLabel.setText(" " + String.format(timeFormat, min, sec) + " ");
244 mainWindow.pack();
245 }
246
247 class PopupListener extends MouseAdapter {
248 private boolean drag = false;
249
250 @Override
251 public void mousePressed(MouseEvent e) {
252 if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0) {
253 drag = true;
254 } else {
255 drag = false;
256 }
257 maybeShowPopup(e);
258 }
259
260 @Override
261 public void mouseReleased(MouseEvent e) {
262 drag = false;
263 maybeShowPopup(e);
264 }
265
266 @Override
267 public void mouseDragged(MouseEvent e) {
268 if (drag) {
269 Component component = (Component) e.getSource();
270 component.setLocation(component.getX() + e.getX(), component.getY() + e.getY());
271 }
272 }
273
274 private void maybeShowPopup(MouseEvent e) {
275 if (e.isPopupTrigger()) {
276 popup.show(e.getComponent(), e.getX(), e.getY());
277 }
278 }
279 }
280
281 class SystemTrayMouseAdapter extends MouseAdapter {
282 @Override
283 public void mousePressed(MouseEvent e) {
284 if (e.isPopupTrigger()) {
285 popup.setLocation(e.getX(), e.getY());
286 popup.setInvoker(popup);
287 popup.setVisible(true);
288 }
289 }
290
291 @Override
292 public void mouseReleased(MouseEvent e) {
293 if (e.isPopupTrigger()) {
294 popup.setLocation(e.getX(), e.getY());
295 popup.setInvoker(popup);
296 popup.setVisible(true);
297 }
298 }
299 }
300
301 class CountdownTask extends SwingWorker<Void, String> {
302 private long until = System.currentTimeMillis() + duration;
303 private long pause = 500;
304 private long warningOrange = 5 * 60 * 1000;
305 private long warningRed = 1 * 60 * 1000;
306
307 @Override
308 protected Void doInBackground() {
309 boolean flackeringState = true;
310 while (!isCancelled() && duration > 0) {
311 duration = until - System.currentTimeMillis();
312 if (duration > warningOrange) {
313 changeColor(Color.GREEN);
314 } else if (duration > warningRed) {
315 changeColor(Color.ORANGE);
316 } else {
317 flackeringState = !(flackeringState);
318 if (flackeringState) {
319 changeColor(Color.RED);
320 } else {
321 changeColor(Color.ORANGE);
322 }
323 }
324 showTimeOnDisplay(duration);
325 try {
326 Thread.sleep(pause);
327 } catch (InterruptedException e) {
328
329 }
330 }
331 if (duration <= 0) {
332 mainLabel.setText(" You're Done! ");
333 try {
334 Thread.sleep(1);
335 } catch (InterruptedException e) {
336
337 }
338 mainWindow.pack();
339 duration = 0;
340 started = false;
341 }
342 return null;
343 }
344 }
345 }