View Javadoc
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   * a microchronometer always on top
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 			// FIXME: use JFrame if not supported
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 			// FIXME: Locate at mouse position
141 			aboutFrame.setLocation(source.getX(), source.getY());
142 			aboutFrame.setVisible(true);
143 		//} else if ("Close Menu".equals(eventText)) {
144 		//	// nothing to do here
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)) { // noch nicht zufriedenstellend...
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 					// nothing to do here
329 				}
330 			}
331 			if (duration <= 0) {
332 				mainLabel.setText(" You're Done! ");
333 				try {
334 					Thread.sleep(1);
335 				} catch (InterruptedException e) {
336 					// nothing to do here
337 				}
338 				mainWindow.pack();
339 				duration = 0;
340 				started = false;
341 			}
342 			return null;
343 		}
344 	}
345 }