package nodebox.versioncheck;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
/**
* Main class. Checks for updates.
*/
public class Updater {
public static final String LAST_UPDATE_CHECK = "NBLastUpdateCheck";
public static final long UPDATE_INTERVAL = 1000 * 60 * 60 * 24; // 1000 milliseconds * 60 seconds * 60 minutes * 24 hours = Every day
private final Host host;
private Icon hostIcon;
private boolean automaticCheck;
private Preferences preferences;
private UpdateChecker updateChecker;
private UpdateDelegate delegate;
private UpdateCheckDialog updateCheckDialog;
public Updater(Host host) {
this.host = host;
automaticCheck = true;
Class hostClass = host.getClass();
this.preferences = Preferences.userNodeForPackage(hostClass);
}
public Host getHost() {
return host;
}
public Icon getHostIcon() {
if (hostIcon != null) return hostIcon;
BufferedImage img = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) img.getGraphics();
Icon icon = new ImageIcon(host.getIconFile());
float factor = 64f / icon.getIconHeight();
g.scale(factor, factor);
icon.paintIcon(null, g, 0, 0);
hostIcon = new ImageIcon(img);
return hostIcon;
}
public UpdateDelegate getDelegate() {
return delegate;
}
public void setDelegate(UpdateDelegate delegate) {
this.delegate = delegate;
}
public UpdateChecker getUpdateChecker() {
return updateChecker;
}
/**
* Notification sent from the application that the updater can do its thing.
*/
public void applicationDidFinishLaunching() {
checkForUpdatesInBackground();
}
public boolean isAutomaticCheck() {
return automaticCheck;
}
/**
* This method is initialized by a user action.
* <p/>
* It will check for updates and reports its findings in a user dialog.
*/
public void checkForUpdates() {
checkForUpdates(true);
}
/**
* This method is initialized by a user action.
* <p/>
* It will check for updates and reports its findings in a user dialog.
*
* @param showProgressWindow if true, shows the progress window.
*/
public void checkForUpdates(boolean showProgressWindow) {
if (updateChecker != null && updateChecker.isAlive()) return;
if (showProgressWindow) {
updateCheckDialog = new UpdateCheckDialog(null, this);
updateCheckDialog.setLocationRelativeTo(null);
updateCheckDialog.setVisible(true);
}
updateChecker = new UpdateChecker(this, false);
updateChecker.start();
}
/**
* This method gets run when automatically checking for updates.
* <p/>
* If no updates are found, results are silently discarded. If an update is found, the update dialog will show.
*/
public void checkForUpdatesInBackground() {
if (shouldCheckForUpdate()) {
updateChecker = new UpdateChecker(this, true);
updateChecker.start();
}
}
/**
* Determines if the last check was far enough in the past to justify a new check.
*
* @return if the enough time has passed since the last check.
*/
public boolean shouldCheckForUpdate() {
long lastTime = getPreferences().getLong(LAST_UPDATE_CHECK, 0);
long deltaTime = System.currentTimeMillis() - lastTime;
return deltaTime > UPDATE_INTERVAL;
}
public Preferences getPreferences() {
return preferences;
}
/**
* The update checker completed the check without error.
*
* @param checker the update checker
* @param appcast the appcast
*/
public void checkCompleted(UpdateChecker checker, Appcast appcast) {
hideUpdateCheckDialog();
// Delegate method.
if (delegate != null)
if (delegate.checkCompleted(checker, appcast))
return;
// Store last check update check time.
getPreferences().putLong(LAST_UPDATE_CHECK, System.currentTimeMillis());
try {
getPreferences().flush();
} catch (BackingStoreException e) {
throw new RuntimeException("Error while storing preferences", e);
}
}
public void checkerFoundValidUpdate(UpdateChecker checker, Appcast appcast) {
// Delegate method.
if (delegate != null)
if (delegate.checkerFoundValidUpdate(checker, appcast))
return;
UpdateAlert alert = new UpdateAlert(this, appcast);
alert.setLocationRelativeTo(null);
alert.setVisible(true);
}
public void checkerDetectedLatestVersion(UpdateChecker checker, Appcast appcast) {
// Delegate method.
if (delegate != null)
if (delegate.checkerDetectedLatestVersion(checker, appcast))
return;
// Show a message that you're using the latest version if you've explicitly checked for updates.
if (!checker.isSilent()) {
JOptionPane.showMessageDialog(null, "<html><b>You're up to date!</b><br>\n" + host.getName() + " " + host.getVersion() + " is the latest version available.", "", JOptionPane.INFORMATION_MESSAGE, getHostIcon());
}
}
public void checkerEncounteredError(UpdateChecker checker, Throwable t) {
hideUpdateCheckDialog();
// Delegate method.
if (delegate != null)
if (delegate.checkerEncounteredError(checker, t))
return;
if (checker.isSilent()) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Update checker encountered error.", t);
} else {
throw new RuntimeException(t);
}
}
public void waitForCheck(int timeoutMillis) {
try {
updateChecker.join(timeoutMillis);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (updateChecker.isAlive()) {
updateChecker.interrupt();
}
}
public void cancelUpdateCheck() {
if (updateChecker != null && updateChecker.isAlive()) {
updateChecker.interrupt();
}
}
private void hideUpdateCheckDialog() {
if (updateCheckDialog != null) {
updateCheckDialog.setVisible(false);
updateCheckDialog = null;
}
}
}