From d3449f45184407e654906aaef8c674f18b87a5d3 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Wed, 7 Jul 2004 08:42:40 +0000 Subject: [PATCH] Beginnings of an interface to display download and patching status. --- .../threerings/getdown/data/Application.java | 52 ++++++++++- .../getdown/launcher/Downloader.java | 4 +- .../threerings/getdown/launcher/Getdown.java | 78 ++++++++++++++-- .../getdown/launcher/StatusPanel.java | 93 +++++++++++++++++++ 4 files changed, 217 insertions(+), 10 deletions(-) create mode 100644 src/java/com/threerings/getdown/launcher/StatusPanel.java diff --git a/src/java/com/threerings/getdown/data/Application.java b/src/java/com/threerings/getdown/data/Application.java index 7e4b97f..7e1a1b3 100644 --- a/src/java/com/threerings/getdown/data/Application.java +++ b/src/java/com/threerings/getdown/data/Application.java @@ -1,8 +1,10 @@ // -// $Id: Application.java,v 1.5 2004/07/06 09:46:35 mdb Exp $ +// $Id: Application.java,v 1.6 2004/07/07 08:42:40 mdb Exp $ package com.threerings.getdown.data; +import java.awt.Rectangle; + import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -42,6 +44,23 @@ public class Application /** The name of our target version file. */ public static final String VERSION_FILE = "version.txt"; + /** Used to communicate information about the UI displayed when + * updating the application. */ + public static class UpdateInterface + { + /** The human readable name of this application. */ + public String name; + + /** The dimensions of the progress bar. */ + public Rectangle progress; + + /** The dimensions of the status display. */ + public Rectangle status; + + /** The path (relative to the appdir) to the background image. */ + public String background; + } + /** * Creates an application instance which records the location of the * getdown.txt configuration file from the supplied @@ -91,10 +110,13 @@ public class Application * discovered later, the caller can use the application base to * download a new config.txt file and try again. * + * @return a configured UpdateInterface instance that will be used to + * configure the update UI. + * * @exception IOException thrown if there is an error reading the file * or an error encountered during its parsing. */ - public void init () + public UpdateInterface init () throws IOException { // parse our configuration file @@ -194,6 +216,15 @@ public class Application _appargs.add(appargs[ii]); } } + + // parse and return our application config + UpdateInterface ui = new UpdateInterface(); + ui.name = (String)cdata.get("ui.name"); + ui.progress = parseRect( + "ui.progress", (String)cdata.get("ui.progress")); + ui.status = parseRect("ui.progress", (String)cdata.get("ui.status")); + ui.background = (String)cdata.get("ui.background"); + return ui; } /** @@ -362,6 +393,8 @@ public class Application StreamUtil.close(fin); } + // next parse any custom user interface information + // finally let the caller know if we need an update return _version != _targetVersion; } @@ -469,6 +502,21 @@ public class Application return new Resource(path, getRemoteURL(path), getLocalPath(path)); } + /** Used to parse rectangle specifications from the config file. */ + protected Rectangle parseRect (String name, String value) + { + if (!StringUtil.blank(value)) { + int[] v = StringUtil.parseIntArray(value); + if (v != null && v.length == 4) { + return new Rectangle(v[0], v[1], v[2], v[3]); + } else { + Log.warning("Ignoring invalid '" + name + "' config '" + + value + "'."); + } + } + return null; + } + protected File _appdir; protected File _config; protected Digest _digest; diff --git a/src/java/com/threerings/getdown/launcher/Downloader.java b/src/java/com/threerings/getdown/launcher/Downloader.java index fab5f82..2b23f35 100644 --- a/src/java/com/threerings/getdown/launcher/Downloader.java +++ b/src/java/com/threerings/getdown/launcher/Downloader.java @@ -1,5 +1,5 @@ // -// $Id: Downloader.java,v 1.1 2004/07/06 05:13:36 mdb Exp $ +// $Id: Downloader.java,v 1.2 2004/07/07 08:42:40 mdb Exp $ package com.threerings.getdown.launcher; @@ -260,5 +260,5 @@ public class Downloader extends Thread /** The delay in milliseconds between notifying progress observers of * file download progress. */ - protected static final long UPDATE_DELAY = 2500L; + protected static final long UPDATE_DELAY = 500L; } diff --git a/src/java/com/threerings/getdown/launcher/Getdown.java b/src/java/com/threerings/getdown/launcher/Getdown.java index 276c8c8..696c69b 100644 --- a/src/java/com/threerings/getdown/launcher/Getdown.java +++ b/src/java/com/threerings/getdown/launcher/Getdown.java @@ -1,8 +1,15 @@ // -// $Id: Getdown.java,v 1.4 2004/07/06 05:13:36 mdb Exp $ +// $Id: Getdown.java,v 1.5 2004/07/07 08:42:40 mdb Exp $ package com.threerings.getdown.launcher; +import java.awt.BorderLayout; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; + +import javax.imageio.ImageIO; +import javax.swing.JFrame; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -12,6 +19,9 @@ import java.util.List; import org.apache.commons.io.TeeOutputStream; +import com.samskivert.swing.util.SwingUtil; +import com.samskivert.util.StringUtil; + import com.threerings.getdown.Log; import com.threerings.getdown.data.Application; import com.threerings.getdown.data.Resource; @@ -31,10 +41,11 @@ public class Getdown { try { for (int ii = 0; ii < MAX_LOOPS; ii++) { - _app.init(); + _ifc = _app.init(); if (_app.verifyMetadata()) { Log.info("Application requires update."); + update(); } else { Log.info("Metadata verified."); @@ -63,7 +74,6 @@ public class Getdown */ protected void update () { - Log.info("We're needin' an update Cap'n!"); } /** @@ -74,15 +84,20 @@ public class Getdown { final Object lock = new Object(); + // create our user interface + createInterface(); + // create a downloader to download our resources Downloader.Observer obs = new Downloader.Observer() { public void resolvingDownloads () { Log.info("Resolving downloads..."); + _status.setStatus("Resolving downloads..."); } public void downloadProgress (int percent, long remaining) { - Log.info("Download progress " + percent + "% " + - remaining + "s remaining."); + _status.setStatus("Download progress " + percent + "%, " + + remaining + " seconds remaining."); + _status.setProgress(percent); if (percent == 100) { synchronized (lock) { lock.notify(); @@ -91,6 +106,7 @@ public class Getdown } public void downloadFailed (Resource rsrc, Exception e) { + _status.setStatus("Download failed: " + e.getMessage()); Log.warning("Download failed [rsrc=" + rsrc + "]."); Log.logStackTrace(e); synchronized (lock) { @@ -117,14 +133,55 @@ public class Getdown */ protected void launch () { - Log.info("All systems go!"); + if (_status != null) { + _status.setStatus("Launching..."); + } + try { Process proc = _app.createProcess(); + System.exit(0); } catch (IOException ioe) { Log.logStackTrace(ioe); } } + /** + * Creates our user interface, which we avoid doing unless we actually + * have to update something. + */ + protected void createInterface () + { + if (_frame != null) { + return; + } + + Rectangle ppos = (_ifc.progress == null) ? DEFAULT_PPOS : _ifc.progress; + Rectangle spos = (_ifc.status == null) ? DEFAULT_STATUS : _ifc.status; + Rectangle bounds = ppos.union(spos); + bounds.grow(5, 5); + + // if we have a background image, load it up + BufferedImage bgimg = null; + if (!StringUtil.blank(_ifc.background)) { + File bgpath = _app.getLocalPath(_ifc.background); + try { + bgimg = ImageIO.read(bgpath); + bounds.setRect(0, 0, bgimg.getWidth(), bgimg.getHeight()); + } catch (IOException ioe) { + Log.warning("Failed to read UI background [path=" + bgpath + + ", error=" + ioe + "]."); + } + } + + // create our user interface, and display it + _frame = new JFrame(StringUtil.blank(_ifc.name) ? "" : _ifc.name); + _status = new StatusPanel(bounds, bgimg, ppos, spos); + _frame.getContentPane().add(_status, BorderLayout.CENTER); + _frame.pack(); + SwingUtil.centerWindow(_frame); + _frame.show(); + } + public static void main (String[] args) { // ensure the proper parameters are passed @@ -161,6 +218,15 @@ public class Getdown } protected Application _app; + protected Application.UpdateInterface _ifc; + + protected JFrame _frame; + protected StatusPanel _status; protected static final int MAX_LOOPS = 5; + + protected static final Rectangle DEFAULT_PPOS = + new Rectangle(0, 0, 300, 15); + protected static final Rectangle DEFAULT_STATUS = + new Rectangle(0, 20, 300, 200); } diff --git a/src/java/com/threerings/getdown/launcher/StatusPanel.java b/src/java/com/threerings/getdown/launcher/StatusPanel.java new file mode 100644 index 0000000..2e89398 --- /dev/null +++ b/src/java/com/threerings/getdown/launcher/StatusPanel.java @@ -0,0 +1,93 @@ +// +// $Id: StatusPanel.java,v 1.1 2004/07/07 08:42:40 mdb Exp $ + +package com.threerings.getdown.launcher; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; + +import javax.swing.JComponent; + +import com.samskivert.swing.Label; + +/** + * Displays download and patching status. + */ +public class StatusPanel extends JComponent +{ + public StatusPanel (Rectangle bounds, BufferedImage bgimg, + Rectangle ppos, Rectangle spos) + { + _bgimg = bgimg; + _psize = new Dimension(bounds.width, bounds.height); + _ppos = ppos; + _spos = spos; + } + + /** + * Adjusts the progress display to the specified percentage. + */ + public void setProgress (int percent) + { + _progress = percent; + repaint(); + } + + /** + * Displays the specified status string. + */ + public void setStatus (String status) + { + _newlab = new Label(status, Color.white, null); + _newlab.setTargetWidth(_spos.width); + repaint(); + } + + // documentation inherited + public void paintComponent (Graphics g) + { + super.paintComponent(g); + Graphics2D gfx = (Graphics2D)g; + + // if we have a new label; lay it out + if (_newlab != null) { + _newlab.layout(gfx); + _label = _newlab; + _newlab = null; + } + + if (_bgimg != null) { + gfx.drawImage(_bgimg, 0, 0, null); + } else { + gfx.fillRect(0, 0, getWidth(), getHeight()); + } + + gfx.setColor(Color.blue); + gfx.fillRect(_ppos.x, _ppos.y, _progress * _ppos.width / 100, + _ppos.height); + + gfx.setColor(Color.white); + gfx.draw(_ppos); + + if (_label != null) { + _label.render(gfx, _spos.x, _spos.y); + } + } + + // documentation inherited + public Dimension getPreferredSize () + { + return _psize; + } + + protected BufferedImage _bgimg; + protected Dimension _psize; + protected Rectangle _ppos, _spos; + + protected int _progress = 0; + protected Label _label, _newlab; +}