Skip to content

Commit

Permalink
Ensure UI components are accessed on EDT.
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanSweet committed May 25, 2017
1 parent 2f3cb01 commit 219633e
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 66 deletions.
112 changes: 56 additions & 56 deletions src/com/esotericsoftware/clippy/BreakWarning.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

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

import java.awt.EventQueue;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Timer;
import java.util.TimerTask;

import javax.sound.sampled.AudioInputStream;
Expand All @@ -16,6 +16,7 @@
import javax.sound.sampled.FloatControl;

import com.esotericsoftware.clippy.Win.LASTINPUTINFO;
import com.esotericsoftware.clippy.util.EventQueueRepeat;
import com.esotericsoftware.clippy.util.Util;

public class BreakWarning {
Expand All @@ -27,9 +28,11 @@ public class BreakWarning {
volatile ProgressBar progressBar;
Clip startClip, flashClip, endClip;
volatile boolean disabled;
Timer timer;

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

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

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

EventQueue.invokeLater(new Runnable() {
public void run () {
new EventQueueRepeat() {
float indeterminateMillis = 5000;
float volume = 0.05f;

protected void start () {
progressBar = new ProgressBar("");
progressBar.clickToDispose = false;
progressBar.red("");
clippy.tray.updateTooltip("Clippy - Take a break!");
if (clippy.config.breakReminderMinutes > 0)
clippy.tray.balloon("Clippy", "Take a break!", 30000);
else {
playClip(startClip, 1);
progressBar.setVisible(true);
}
new Thread("BreakWarning Dialog") {
{
setDaemon(true);
}
}

protected boolean repeat () {
long inactiveMillis = getInactiveMillis(false);
long inactiveMinutes = inactiveMillis / 1000 / 60;
if (inactiveMinutes >= clippy.config.breakResetMinutes) return true;

public void run () {
float indeterminateMillis = 5000;
float volume = 0.05f;
while (true) {
long inactiveMillis = getInactiveMillis(false);
long inactiveMinutes = inactiveMillis / 1000 / 60;
if (inactiveMinutes >= clippy.config.breakResetMinutes) break;

float percent = 1 - inactiveMillis / (float)(clippy.config.breakResetMinutes * 60 * 1000);
String message;
if (percent < 0.75f) {
indeterminateMillis = 0;
message = "Break: " + formatTimeSeconds(clippy.config.breakResetMinutes * 60 * 1000 - inactiveMillis);
progressBar.setVisible(true);
} else
message = "Active: " + formatTimeMinutes(System.currentTimeMillis() - lastBreakTime);
progressBar.progressBar.setString(message);

indeterminateMillis -= 100;
if (indeterminateMillis > 0) {
if (!progressBar.progressBar.isIndeterminate()) {
if (!progressBar.isVisible()) {
// First time after balloon.
playClip(startClip, 1);
progressBar.setVisible(true);
} else {
// Every breakReminderMinutes.
playClip(flashClip, volume);
volume += 0.1f;
}
progressBar.progressBar.setIndeterminate(true);
if (INFO) info("Break reminder.");
}
} else {
if (clippy.config.breakReminderMinutes > 0 && percent >= 0.99f
&& indeterminateMillis < -clippy.config.breakReminderMinutes * 60 * 1000) indeterminateMillis = 5000;
progressBar.setProgress(percent); // Sets indeterminate to false.
progressBar.toFront();
progressBar.setAlwaysOnTop(true);
}
Util.sleep(100);
float percent = 1 - inactiveMillis / (float)(clippy.config.breakResetMinutes * 60 * 1000);
String message;
if (percent < 0.75f) {
indeterminateMillis = 0;
message = "Break: " + formatTimeSeconds(clippy.config.breakResetMinutes * 60 * 1000 - inactiveMillis);
progressBar.setVisible(true);
} else
message = "Active: " + formatTimeMinutes(System.currentTimeMillis() - lastBreakTime);
progressBar.progressBar.setString(message);

indeterminateMillis -= 100;
if (indeterminateMillis > 0) {
if (!progressBar.progressBar.isIndeterminate()) {
if (!progressBar.isVisible()) {
// First time after balloon.
playClip(startClip, 1);
progressBar.setVisible(true);
} else {
// Every breakReminderMinutes.
playClip(flashClip, volume);
volume += 0.1f;
}
lastBreakTime = System.currentTimeMillis();
playClip(endClip, 1);
progressBar.done("Break complete!", 2000);
progressBar = null;
if (INFO) info("Break complete!");
progressBar.progressBar.setIndeterminate(true);
if (INFO) info("Break reminder.");
}
}.start();
} else {
if (clippy.config.breakReminderMinutes > 0 && percent >= 0.99f
&& indeterminateMillis < -clippy.config.breakReminderMinutes * 60 * 1000) indeterminateMillis = 5000;
progressBar.setProgress(percent); // Sets indeterminate to false.
progressBar.toFront();
progressBar.setAlwaysOnTop(true);
}
return false;
}

protected void end () {
lastBreakTime = System.currentTimeMillis();
playClip(endClip, 1);
progressBar.done("Break complete!", 2000);
progressBar = null;
if (INFO) info("Break complete!");
}
});
}.run(100);
}

void playClip (Clip clip, float volume) {
Expand Down
79 changes: 70 additions & 9 deletions src/com/esotericsoftware/clippy/PhilipsHue.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

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

import java.awt.EventQueue;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
Expand Down Expand Up @@ -79,9 +80,17 @@ void start () {
hue.setDeviceName(name);

hue.getNotificationManager().registerSDKListener(new PHSDKListener() {
private boolean started;
private volatile boolean started;

public void onAccessPointsFound (List<PHAccessPoint> accessPoints) {
public void onAccessPointsFound (final List<PHAccessPoint> accessPoints) {
EventQueue.invokeLater(new Runnable() {
public void run () {
onAccessPointsFoundEDT(accessPoints);
}
});
}

void onAccessPointsFoundEDT (List<PHAccessPoint> accessPoints) {
if (progress == null) {
progress = new ProgressBar("");
progress.setVisible(true);
Expand All @@ -102,7 +111,17 @@ public void onAccessPointsFound (List<PHAccessPoint> accessPoints) {
}
}

public void onAuthenticationRequired (PHAccessPoint accessPoint) {
// ---

public void onAuthenticationRequired (final PHAccessPoint accessPoint) {
EventQueue.invokeLater(new Runnable() {
public void run () {
onAuthenticationRequired(accessPoint);
}
});
}

void onAuthenticationRequiredEDT (PHAccessPoint accessPoint) {
if (INFO) {
info("Philips Hue authentication required: " + accessPoint.getIpAddress());
info("Press the Philips Hue link button...");
Expand All @@ -115,7 +134,17 @@ public void onAuthenticationRequired (PHAccessPoint accessPoint) {
hue.startPushlinkAuthentication(accessPoint);
}

public void onBridgeConnected (PHBridge bridge, String username) {
// ---

public void onBridgeConnected (final PHBridge bridge, final String username) {
EventQueue.invokeLater(new Runnable() {
public void run () {
onBridgeConnectedEDT(bridge, username);
}
});
}

void onBridgeConnectedEDT (PHBridge bridge, String username) {
if (INFO) info("Philips Hue bridge connected: " + username);
if (progress != null) {
progress.done("Philips Hue bridge connected!", 2000);
Expand All @@ -140,6 +169,8 @@ public void onBridgeConnected (PHBridge bridge, String username) {
}
}

// ---

public void onConnectionLost (PHAccessPoint accessPoint) {
if (WARN) warn("Philips Hue connection lost.");
}
Expand All @@ -152,12 +183,24 @@ public void onCacheUpdated (List<Integer> messageTypes, PHBridge bridge) {
if (TRACE) trace("Philips Hue cache updated: " + messageTypes);
}

public void onError (int code, String message) {
// ---

public void onError (final int code, final String message) {
if (code == PHMessageType.PUSHLINK_BUTTON_NOT_PRESSED) {
if (TRACE) trace("Philips Hue error: " + message + " (" + code + ")");
return;
}
if (ERROR) error("Philips Hue error: " + message + " (" + code + ")");
if (!started) {
EventQueue.invokeLater(new Runnable() {
public void run () {
onErrorEDT(code, message);
}
});
}
}

void onErrorEDT (int code, String message) {
if (!started) {
if (progress == null) {
progress = new ProgressBar("");
Expand All @@ -169,6 +212,8 @@ public void onError (int code, String message) {
}
}

// ---

public void onParsingErrors (List<PHHueParsingError> errors) {
if (ERROR) {
error("Philips Hue parsing errors:");
Expand All @@ -183,17 +228,33 @@ public void onParsingErrors (List<PHHueParsingError> errors) {
if (clippy.data.philipsHueIP != null) {
if (DEBUG) debug("Connecting to Philips Hue bridge: " + clippy.data.philipsHueUser + " @ " + clippy.data.philipsHueIP);
if (clippy.data.philipsHueUser == null) {
progress = new ProgressBar("Connecting to Philips Hue: " + clippy.data.philipsHueIP);
progress.setVisible(true);
try {
EventQueue.invokeAndWait(new Runnable() {
public void run () {
progress = new ProgressBar("Connecting to Philips Hue: " + clippy.data.philipsHueIP);
progress.setVisible(true);
}
});
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
PHAccessPoint accessPoint = new PHAccessPoint();
accessPoint.setIpAddress(clippy.data.philipsHueIP);
accessPoint.setUsername(clippy.data.philipsHueUser);
hue.connect(accessPoint);
} else {
if (INFO) info("Searching for Philips Hue bridges...");
progress = new ProgressBar("Searching for Philips Hue bridges...");
progress.setVisible(true);
try {
EventQueue.invokeAndWait(new Runnable() {
public void run () {
progress = new ProgressBar("Searching for Philips Hue bridges...");
progress.setVisible(true);
}
});
} catch (Exception ex) {
throw new RuntimeException(ex);
}
PHBridgeSearchManager search = (PHBridgeSearchManager)hue.getSDKService(PHHueSDK.SEARCH_BRIDGE);
search.search(true, true);
}
Expand Down
5 changes: 4 additions & 1 deletion src/com/esotericsoftware/clippy/ProgressBar.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@ public void dispose () {

public void setProgress (final float progress) {
this.progress = progress;
EventQueue.invokeLater(updateProgress);
if (EventQueue.isDispatchThread())
updateProgress.run();
else
EventQueue.invokeLater(updateProgress);
}

public void green (String message) {
Expand Down
42 changes: 42 additions & 0 deletions src/com/esotericsoftware/clippy/util/EventQueueRepeat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

package com.esotericsoftware.clippy.util;

import java.awt.EventQueue;
import java.util.TimerTask;

public abstract class EventQueueRepeat {
final Runnable repeatRunnable = new Runnable() {
public void run () {
if (repeat()) {
repeatTask.cancel();
end();
}
}
};

final TimerTask repeatTask = new TimerTask() {
public void run () {
try {
EventQueue.invokeLater(repeatRunnable);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
};

public void run (final int delay) {
EventQueue.invokeLater(new Runnable() {
public void run () {
start();
Util.timer.schedule(repeatTask, delay, delay);
}
});
}

abstract protected void start ();

/** Returns true when finished. */
abstract protected boolean repeat ();

abstract protected void end ();
}

0 comments on commit 219633e

Please sign in to comment.