WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 219633e

Browse files
committed
Ensure UI components are accessed on EDT.
1 parent 2f3cb01 commit 219633e

File tree

4 files changed

+172
-66
lines changed

4 files changed

+172
-66
lines changed

src/com/esotericsoftware/clippy/BreakWarning.java

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33

44
import static com.esotericsoftware.minlog.Log.*;
55

6-
import java.awt.EventQueue;
76
import java.io.BufferedInputStream;
87
import java.io.FileInputStream;
98
import java.io.IOException;
109
import java.io.InputStream;
10+
import java.util.Timer;
1111
import java.util.TimerTask;
1212

1313
import javax.sound.sampled.AudioInputStream;
@@ -16,6 +16,7 @@
1616
import javax.sound.sampled.FloatControl;
1717

1818
import com.esotericsoftware.clippy.Win.LASTINPUTINFO;
19+
import com.esotericsoftware.clippy.util.EventQueueRepeat;
1920
import com.esotericsoftware.clippy.util.Util;
2021

2122
public class BreakWarning {
@@ -27,9 +28,11 @@ public class BreakWarning {
2728
volatile ProgressBar progressBar;
2829
Clip startClip, flashClip, endClip;
2930
volatile boolean disabled;
31+
Timer timer;
3032

3133
public BreakWarning () {
3234
if (clippy.config.breakWarningMinutes <= 0) return;
35+
timer = new Timer("BreakWarning", true);
3336

3437
if (clippy.config.breakStartSound != null) startClip = loadClip(clippy.config.breakStartSound);
3538
if (clippy.config.breakFlashSound != null) flashClip = loadClip(clippy.config.breakFlashSound);
@@ -59,76 +62,73 @@ public void run () {
5962
}
6063

6164
void showBreakDialog () {
62-
clippy.tray.updateTooltip("Clippy - Take a break!");
6365
if (INFO) info("Break needed.");
6466

65-
EventQueue.invokeLater(new Runnable() {
66-
public void run () {
67+
new EventQueueRepeat() {
68+
float indeterminateMillis = 5000;
69+
float volume = 0.05f;
70+
71+
protected void start () {
6772
progressBar = new ProgressBar("");
6873
progressBar.clickToDispose = false;
6974
progressBar.red("");
75+
clippy.tray.updateTooltip("Clippy - Take a break!");
7076
if (clippy.config.breakReminderMinutes > 0)
7177
clippy.tray.balloon("Clippy", "Take a break!", 30000);
7278
else {
7379
playClip(startClip, 1);
7480
progressBar.setVisible(true);
7581
}
76-
new Thread("BreakWarning Dialog") {
77-
{
78-
setDaemon(true);
79-
}
82+
}
83+
84+
protected boolean repeat () {
85+
long inactiveMillis = getInactiveMillis(false);
86+
long inactiveMinutes = inactiveMillis / 1000 / 60;
87+
if (inactiveMinutes >= clippy.config.breakResetMinutes) return true;
8088

81-
public void run () {
82-
float indeterminateMillis = 5000;
83-
float volume = 0.05f;
84-
while (true) {
85-
long inactiveMillis = getInactiveMillis(false);
86-
long inactiveMinutes = inactiveMillis / 1000 / 60;
87-
if (inactiveMinutes >= clippy.config.breakResetMinutes) break;
88-
89-
float percent = 1 - inactiveMillis / (float)(clippy.config.breakResetMinutes * 60 * 1000);
90-
String message;
91-
if (percent < 0.75f) {
92-
indeterminateMillis = 0;
93-
message = "Break: " + formatTimeSeconds(clippy.config.breakResetMinutes * 60 * 1000 - inactiveMillis);
94-
progressBar.setVisible(true);
95-
} else
96-
message = "Active: " + formatTimeMinutes(System.currentTimeMillis() - lastBreakTime);
97-
progressBar.progressBar.setString(message);
98-
99-
indeterminateMillis -= 100;
100-
if (indeterminateMillis > 0) {
101-
if (!progressBar.progressBar.isIndeterminate()) {
102-
if (!progressBar.isVisible()) {
103-
// First time after balloon.
104-
playClip(startClip, 1);
105-
progressBar.setVisible(true);
106-
} else {
107-
// Every breakReminderMinutes.
108-
playClip(flashClip, volume);
109-
volume += 0.1f;
110-
}
111-
progressBar.progressBar.setIndeterminate(true);
112-
if (INFO) info("Break reminder.");
113-
}
114-
} else {
115-
if (clippy.config.breakReminderMinutes > 0 && percent >= 0.99f
116-
&& indeterminateMillis < -clippy.config.breakReminderMinutes * 60 * 1000) indeterminateMillis = 5000;
117-
progressBar.setProgress(percent); // Sets indeterminate to false.
118-
progressBar.toFront();
119-
progressBar.setAlwaysOnTop(true);
120-
}
121-
Util.sleep(100);
89+
float percent = 1 - inactiveMillis / (float)(clippy.config.breakResetMinutes * 60 * 1000);
90+
String message;
91+
if (percent < 0.75f) {
92+
indeterminateMillis = 0;
93+
message = "Break: " + formatTimeSeconds(clippy.config.breakResetMinutes * 60 * 1000 - inactiveMillis);
94+
progressBar.setVisible(true);
95+
} else
96+
message = "Active: " + formatTimeMinutes(System.currentTimeMillis() - lastBreakTime);
97+
progressBar.progressBar.setString(message);
98+
99+
indeterminateMillis -= 100;
100+
if (indeterminateMillis > 0) {
101+
if (!progressBar.progressBar.isIndeterminate()) {
102+
if (!progressBar.isVisible()) {
103+
// First time after balloon.
104+
playClip(startClip, 1);
105+
progressBar.setVisible(true);
106+
} else {
107+
// Every breakReminderMinutes.
108+
playClip(flashClip, volume);
109+
volume += 0.1f;
122110
}
123-
lastBreakTime = System.currentTimeMillis();
124-
playClip(endClip, 1);
125-
progressBar.done("Break complete!", 2000);
126-
progressBar = null;
127-
if (INFO) info("Break complete!");
111+
progressBar.progressBar.setIndeterminate(true);
112+
if (INFO) info("Break reminder.");
128113
}
129-
}.start();
114+
} else {
115+
if (clippy.config.breakReminderMinutes > 0 && percent >= 0.99f
116+
&& indeterminateMillis < -clippy.config.breakReminderMinutes * 60 * 1000) indeterminateMillis = 5000;
117+
progressBar.setProgress(percent); // Sets indeterminate to false.
118+
progressBar.toFront();
119+
progressBar.setAlwaysOnTop(true);
120+
}
121+
return false;
122+
}
123+
124+
protected void end () {
125+
lastBreakTime = System.currentTimeMillis();
126+
playClip(endClip, 1);
127+
progressBar.done("Break complete!", 2000);
128+
progressBar = null;
129+
if (INFO) info("Break complete!");
130130
}
131-
});
131+
}.run(100);
132132
}
133133

134134
void playClip (Clip clip, float volume) {

src/com/esotericsoftware/clippy/PhilipsHue.java

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import static com.esotericsoftware.minlog.Log.*;
55

6+
import java.awt.EventQueue;
67
import java.io.IOException;
78
import java.io.InputStream;
89
import java.io.InputStreamReader;
@@ -79,9 +80,17 @@ void start () {
7980
hue.setDeviceName(name);
8081

8182
hue.getNotificationManager().registerSDKListener(new PHSDKListener() {
82-
private boolean started;
83+
private volatile boolean started;
8384

84-
public void onAccessPointsFound (List<PHAccessPoint> accessPoints) {
85+
public void onAccessPointsFound (final List<PHAccessPoint> accessPoints) {
86+
EventQueue.invokeLater(new Runnable() {
87+
public void run () {
88+
onAccessPointsFoundEDT(accessPoints);
89+
}
90+
});
91+
}
92+
93+
void onAccessPointsFoundEDT (List<PHAccessPoint> accessPoints) {
8594
if (progress == null) {
8695
progress = new ProgressBar("");
8796
progress.setVisible(true);
@@ -102,7 +111,17 @@ public void onAccessPointsFound (List<PHAccessPoint> accessPoints) {
102111
}
103112
}
104113

105-
public void onAuthenticationRequired (PHAccessPoint accessPoint) {
114+
// ---
115+
116+
public void onAuthenticationRequired (final PHAccessPoint accessPoint) {
117+
EventQueue.invokeLater(new Runnable() {
118+
public void run () {
119+
onAuthenticationRequired(accessPoint);
120+
}
121+
});
122+
}
123+
124+
void onAuthenticationRequiredEDT (PHAccessPoint accessPoint) {
106125
if (INFO) {
107126
info("Philips Hue authentication required: " + accessPoint.getIpAddress());
108127
info("Press the Philips Hue link button...");
@@ -115,7 +134,17 @@ public void onAuthenticationRequired (PHAccessPoint accessPoint) {
115134
hue.startPushlinkAuthentication(accessPoint);
116135
}
117136

118-
public void onBridgeConnected (PHBridge bridge, String username) {
137+
// ---
138+
139+
public void onBridgeConnected (final PHBridge bridge, final String username) {
140+
EventQueue.invokeLater(new Runnable() {
141+
public void run () {
142+
onBridgeConnectedEDT(bridge, username);
143+
}
144+
});
145+
}
146+
147+
void onBridgeConnectedEDT (PHBridge bridge, String username) {
119148
if (INFO) info("Philips Hue bridge connected: " + username);
120149
if (progress != null) {
121150
progress.done("Philips Hue bridge connected!", 2000);
@@ -140,6 +169,8 @@ public void onBridgeConnected (PHBridge bridge, String username) {
140169
}
141170
}
142171

172+
// ---
173+
143174
public void onConnectionLost (PHAccessPoint accessPoint) {
144175
if (WARN) warn("Philips Hue connection lost.");
145176
}
@@ -152,12 +183,24 @@ public void onCacheUpdated (List<Integer> messageTypes, PHBridge bridge) {
152183
if (TRACE) trace("Philips Hue cache updated: " + messageTypes);
153184
}
154185

155-
public void onError (int code, String message) {
186+
// ---
187+
188+
public void onError (final int code, final String message) {
156189
if (code == PHMessageType.PUSHLINK_BUTTON_NOT_PRESSED) {
157190
if (TRACE) trace("Philips Hue error: " + message + " (" + code + ")");
158191
return;
159192
}
160193
if (ERROR) error("Philips Hue error: " + message + " (" + code + ")");
194+
if (!started) {
195+
EventQueue.invokeLater(new Runnable() {
196+
public void run () {
197+
onErrorEDT(code, message);
198+
}
199+
});
200+
}
201+
}
202+
203+
void onErrorEDT (int code, String message) {
161204
if (!started) {
162205
if (progress == null) {
163206
progress = new ProgressBar("");
@@ -169,6 +212,8 @@ public void onError (int code, String message) {
169212
}
170213
}
171214

215+
// ---
216+
172217
public void onParsingErrors (List<PHHueParsingError> errors) {
173218
if (ERROR) {
174219
error("Philips Hue parsing errors:");
@@ -183,17 +228,33 @@ public void onParsingErrors (List<PHHueParsingError> errors) {
183228
if (clippy.data.philipsHueIP != null) {
184229
if (DEBUG) debug("Connecting to Philips Hue bridge: " + clippy.data.philipsHueUser + " @ " + clippy.data.philipsHueIP);
185230
if (clippy.data.philipsHueUser == null) {
186-
progress = new ProgressBar("Connecting to Philips Hue: " + clippy.data.philipsHueIP);
187-
progress.setVisible(true);
231+
try {
232+
EventQueue.invokeAndWait(new Runnable() {
233+
public void run () {
234+
progress = new ProgressBar("Connecting to Philips Hue: " + clippy.data.philipsHueIP);
235+
progress.setVisible(true);
236+
}
237+
});
238+
} catch (Exception ex) {
239+
throw new RuntimeException(ex);
240+
}
188241
}
189242
PHAccessPoint accessPoint = new PHAccessPoint();
190243
accessPoint.setIpAddress(clippy.data.philipsHueIP);
191244
accessPoint.setUsername(clippy.data.philipsHueUser);
192245
hue.connect(accessPoint);
193246
} else {
194247
if (INFO) info("Searching for Philips Hue bridges...");
195-
progress = new ProgressBar("Searching for Philips Hue bridges...");
196-
progress.setVisible(true);
248+
try {
249+
EventQueue.invokeAndWait(new Runnable() {
250+
public void run () {
251+
progress = new ProgressBar("Searching for Philips Hue bridges...");
252+
progress.setVisible(true);
253+
}
254+
});
255+
} catch (Exception ex) {
256+
throw new RuntimeException(ex);
257+
}
197258
PHBridgeSearchManager search = (PHBridgeSearchManager)hue.getSDKService(PHHueSDK.SEARCH_BRIDGE);
198259
search.search(true, true);
199260
}

src/com/esotericsoftware/clippy/ProgressBar.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,10 @@ public void dispose () {
107107

108108
public void setProgress (final float progress) {
109109
this.progress = progress;
110-
EventQueue.invokeLater(updateProgress);
110+
if (EventQueue.isDispatchThread())
111+
updateProgress.run();
112+
else
113+
EventQueue.invokeLater(updateProgress);
111114
}
112115

113116
public void green (String message) {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
2+
package com.esotericsoftware.clippy.util;
3+
4+
import java.awt.EventQueue;
5+
import java.util.TimerTask;
6+
7+
public abstract class EventQueueRepeat {
8+
final Runnable repeatRunnable = new Runnable() {
9+
public void run () {
10+
if (repeat()) {
11+
repeatTask.cancel();
12+
end();
13+
}
14+
}
15+
};
16+
17+
final TimerTask repeatTask = new TimerTask() {
18+
public void run () {
19+
try {
20+
EventQueue.invokeLater(repeatRunnable);
21+
} catch (Exception ex) {
22+
throw new RuntimeException(ex);
23+
}
24+
}
25+
};
26+
27+
public void run (final int delay) {
28+
EventQueue.invokeLater(new Runnable() {
29+
public void run () {
30+
start();
31+
Util.timer.schedule(repeatTask, delay, delay);
32+
}
33+
});
34+
}
35+
36+
abstract protected void start ();
37+
38+
/** Returns true when finished. */
39+
abstract protected boolean repeat ();
40+
41+
abstract protected void end ();
42+
}

0 commit comments

Comments
 (0)