import java.util.*; import java.awt.*; public class AlarmTimer extends Canvas implements Runnable { protected Thread timerThread = null; protected String displayStr; // "00h:00m:00s" protected long stopTimeMillis; protected Date currentTime; protected int tickInterval; // in ms protected int timerDays; protected int timerHours; protected int timerMinutes; protected int timerSeconds; protected int width, height; // AlarmTimer dimensions protected AlarmEventListener tickListener = null; protected AlarmEventListener stopListener = null; //------------------------------------------------------------------- // Convenience constructors //------------------------------------------------------------------- // (seconds) public AlarmTimer(int secs) { this(0,0,0,secs); } // (minutes, seconds) public AlarmTimer(int mins, int secs) { this(0, 0, mins, secs); } // (hours, minutes, seconds) public AlarmTimer(int hours, int mins, int secs) { this(0,hours, mins, secs); } // True constructor public AlarmTimer(int days, int hours, int minutes, int seconds) { timerDays = days; timerHours = hours; timerMinutes = minutes; timerSeconds = seconds; timerThread = new Thread(this); } //------------------------------------------------------------------- // Start the countdown //------------------------------------------------------------------- public void start() { // calculate the time OFFSET from now.. (in milliseconds) stopTimeMillis = timerSeconds + timerMinutes*60 + timerHours*3600 + timerDays*3600*24 ; stopTimeMillis *= 1000; // calculate absolute time by adding current time stopTimeMillis += new Date().getTime(); // start timer logic timerThread.start(); } //------------------------------------------------------------------- // Stop the countdown //------------------------------------------------------------------- public void stop() { timerThread.stop(); } //------------------------------------------------------------------- // The heart of the timer: //------------------------------------------------------------------- public void run() { long timeLeft; int lastSecond=0, thisSecond=0; currentTime = new Date(); timeLeft = stopTimeMillis - currentTime.getTime(); while ( timeLeft > 0) { // display the timer displayTimer(timeLeft); // see if we have to fire an interval event thisSecond = (int) (timeLeft/1000); if ( thisSecond % tickInterval == 0 ) { // if the interval = 1 sec, then we have to add a special // check to avoid re-triggering on the same second if ( lastSecond != thisSecond ) { fireTickEvent(); lastSecond = thisSecond; } } // sleep for a short while (if too coarse, timer seconds become rough) delay( 200 ); currentTime = new Date(); timeLeft = stopTimeMillis - currentTime.getTime(); } // our timer has run to completion: fire the 'stopped' event. fireStoppedEvent(); } //------------------------------------------------------------------- // The AlarmTimer's paint() method //------------------------------------------------------------------- public void paint (Graphics g) { FontMetrics fm; Dimension dims; if ( displayStr != null ) { dims = getSize(); width = dims.width; height = dims.height; fm = g.getFontMetrics(); drawCenteredString(g, displayStr, width, height, fm); } } //------------------------------------------------------------------- // Render a String centered within a given rectangle //------------------------------------------------------------------- public void drawCenteredString(Graphics g, String str, int width, int height, FontMetrics fm) { Dimension rect = new Dimension(width,height); drawCenteredString(g, str, rect, fm); } //------------------------------------------------------------------- // utility method to render a String centered inside some rectangle //------------------------------------------------------------------- public void drawCenteredString(Graphics g, String str, Dimension rect, FontMetrics fm) { int strWidth, strAscent; int centerBoxX, centerBoxY; strWidth = fm.stringWidth(str); strAscent = fm.getAscent(); centerBoxX = rect.width/2 - strWidth/2; centerBoxY = rect.height/2 + strAscent/2; g.drawString(str, centerBoxX, centerBoxY); } //------------------------------------------------------------------- // get/set methods for "tickInterval" attribute //------------------------------------------------------------------- public void setTickInterval( int seconds ) throws IllegalArgumentException { if ( seconds < 1 ) { throw new IllegalArgumentException("timer tick interval has to be >= 1"); } tickInterval = seconds; } public int getTickInterval() { return tickInterval; } //------------------------------------------------------------------- // AlarmTimer event listener (de)registration methods //------------------------------------------------------------------- public void addAlarmStoppedEventListener( AlarmEventListener listener) throws java.util.TooManyListenersException { if (stopListener != null) { throw new TooManyListenersException( "AlarmTimer already has an alarm stopped event listener!"); } stopListener = listener; } //------------------------------------------------------------------- public void addAlarmTickedEventListener( AlarmEventListener listener) throws java.util.TooManyListenersException { if (tickListener != null) { throw new TooManyListenersException( "AlarmTimer already has an alarm periodic event listener!"); } tickListener = listener; } //------------------------------------------------------------------- public void removeAlarmStoppedEventListener( AlarmEventListener listener) { if (stopListener == null) { throw new IllegalArgumentException( "Can't remove stopped alarm listener because none currently set!"); } if (stopListener != listener) { throw new IllegalArgumentException( "Can't remove stopped alarm listener because <> from currently set!"); } stopListener = null; } //------------------------------------------------------------------- public void removeAlarmTickedEventListener( AlarmEventListener listener) { if (tickListener == null) { throw new IllegalArgumentException( "Can't remove periodic alarm listener because none currently set!"); } if (tickListener != listener) { throw new IllegalArgumentException( "Can't remove periodic alarm listener because <> from currently set!"); } tickListener = null; } //------------------------------------------------------------------- // internal event raising methods //------------------------------------------------------------------- protected void fireStoppedEvent() { AlarmEvent event; Date now = new Date(); if ( stopListener != null ) { event = new AlarmEvent(this, stopTimeMillis - now.getTime() ); stopListener.alarmStopped( event ); } } //------------------------------------------------------------------- protected void fireTickEvent() { AlarmEvent event; Date now = new Date(); if ( tickListener != null ) { event = new AlarmEvent(this, stopTimeMillis - now.getTime() ); tickListener.alarmTicked( event ); } } //------------------------------------------------------------------- // Display timer as "00h:00m:00s" //------------------------------------------------------------------- protected void displayTimer(long time) { StringBuffer timeStr = new StringBuffer(); time /= 1000; int secs = (int) (time % 60); time /= 60; int mins = (int) (time % 60); time /= 60; int hours= (int) (time % 24); time /= 24; hours += time * 24; // add number of days as blocks of 24 hr. timeStr.insert(0, secs + "s"); if ( secs < 10 ) { timeStr.insert (0, "0"); } timeStr.insert(0, mins + "m:"); if ( mins < 10 ) { timeStr.insert (0, "0"); } timeStr.insert(0, hours + "h:"); if ( hours < 10 ) { timeStr.insert (0, "0"); } displayStr = timeStr.toString(); repaint(); } //------------------------------------------------------------------- // delay utility method //------------------------------------------------------------------- protected void delay(int millis) { try { Thread.sleep(millis); } catch (InterruptedException ignored) { ; } } } // End of Class AlarmTimer