Java/J2ME/Timer

Материал из Java эксперт
Перейти к: навигация, поиск

A simple class that shows an example of using a Timer and a TimerTask.

/*
 * Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
 */
import java.lang.*;
import java.io.*;
import java.util.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
/**
 * A simple class that shows an example of using a Timer and
 * a TimerTask.
 *
 * This MIDlet creates two gauges. One gauge gaugeOne,
 * sets elements from low to high. The other, gaugeTwo,
 * set elements from high to low. In effect, this has
 * gaugeOne "going up", and gaugeTwo "going down."
 *
 * The two timers fire at different intervals.
 *
 * There are two commands on our form:
 * 
 * OK: toggles whether the times are active or not.
 * EXIT: exits the MIDlet.
 */
public class TimerMIDlet extends MIDlet implements CommandListener {
    // number of elements in gauge
    final private static int GAUGE_MAX = 10;
    private boolean timersRunning; // tracks state of timers
    private Display myDisplay;     // handle to the display
    private Gauge gaugeOne;        // "going up" gauge
    private Gauge gaugeTwo;        // "going down" gauge
    private Form myScreen;         // form on which to 
                                   // place gauges
    private Command cmdOK;         // OK command
    private Command cmdExit;       // EXIT command
    private Timer timer;
    private MyTimerTask timerTaskOne;
    private MyTimerTask timerTaskTwo;
    /**
     * Internal class that provides a TimerTask.
     */
    private class MyTimerTask extends TimerTask {
        private Gauge myGauge; // reference to gauge
        private boolean goUp;  // if true, go up
        private int num;       // number of times called
        /**
         * Public constructor: stores "direction" and a reference to
         * a gauge.
         */
        public MyTimerTask(Gauge g, boolean up) {
            myGauge = g;
            goUp = up;
        }
        /**
         * As the timer fires, this method is invoked. Set gauge
         * based on goUp
         */
        public void run() {
            num++;
            myGauge.setValue(goUp ?
                             GAUGE_MAX -(num % GAUGE_MAX) :
                             num % GAUGE_MAX);
        }
    }
    /**
     * Public constructor: gets handle to display,
     * creates form, gauges, and commands.
     */
    public TimerMIDlet() {
        myDisplay = Display.getDisplay(this);
        myScreen = new Form("TimerMIDlet");
        gaugeOne = new Gauge("Up Gauge",
                             false,
                             GAUGE_MAX,
                             0);
        myScreen.append(gaugeOne);
        gaugeTwo = new Gauge("Down Gauge",
                             false,
                             GAUGE_MAX,
                             GAUGE_MAX);
        myScreen.append(gaugeTwo);
        cmdOK = new Command("OK", Command.OK, 1);
        cmdExit = new Command("Exit", Command.EXIT, 1);
        myScreen.addCommand(cmdOK);
        myScreen.addCommand(cmdExit);
        myScreen.setCommandListener(this);
    }
    /**
     * Changes the state of timers to/from active to/from
     * not-active.
     */
    private void flipFlop() {
        if (timersRunning) {
            timerTaskOne.cancel();
            timerTaskTwo.cancel();
            timer.cancel();
            timersRunning = false;
        } else {
            timer = new Timer();
            timerTaskOne = new MyTimerTask(gaugeOne, false);
            timerTaskTwo = new MyTimerTask(gaugeTwo, true);
            timer.schedule(timerTaskOne, 0, 1000);
            timer.schedule(timerTaskTwo, 0, 1500);
            timersRunning = true;
        }
    }
    /**
     * Called by the system to start our MIDlet.
    * @exception MIDletStateChangeException
     */
    protected void startApp() throws MIDletStateChangeException {
        myDisplay.setCurrent(myScreen);
        flipFlop();
    }

    /**
     * Called by the system to pause our MIDlet.
     * No actions required by our MIDLet.
     */
    protected void pauseApp() {}
    /**
     * Called by the system to end our MIDlet.
     * No actions required by our MIDLet.
     */
    protected void destroyApp(boolean unconditional) {}
    /***
     * Respond to command selections. Process two commands:
     * 
     * OK: flip flop the timers to/from active
     * EXIT: exit this MIDlet
     * 
     */
    public void commandAction(Command c, Displayable d) {
        if (c == cmdOK) {
            flipFlop();
        } else if (c == cmdExit) {
            destroyApp(false);
            notifyDestroyed();
        }
    }
}





Demonstrate simple animation using a Timer and TimerTask

/*--------------------------------------------------
* Animation.java
*
* Demonstrate simple animation using
* a Timer and TimerTask
*
* Example from the book:     Core J2ME Technology
* Copyright John W. Muchow   http://www.CoreJ2ME.ru
* You may use/modify for any non-commercial purpose
*-------------------------------------------------*/  
import java.util.*;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class Animation extends MIDlet
{
  private Display  display;       // The display
  private AnimationCanvas canvas; // Canvas 
  private Timer tm;               // Timer
  private AnimateTimerTask tt;    // Task
 
  public Animation()
  {
    display = Display.getDisplay(this);
    canvas  = new AnimationCanvas(this);
    // Create task that fires off every 1/10 second    
    tm = new Timer();
    tt = new AnimateTimerTask(canvas);
    tm.schedule(tt, 0, 100);    
  }
 
  protected void startApp()
  {
    display.setCurrent(canvas);
  }
 
  protected void pauseApp()
  { }
  protected void destroyApp(boolean unconditional)
  { }
 
  public void exitMIDlet()
  {
    destroyApp(true);
    notifyDestroyed();
  }
}

/*--------------------------------------------------
* AnimateTimerTask.java
*
* Change location of bouncing ball on the canvas
*
* Example from the book:     Core J2ME Technology
* Copyright John W. Muchow   http://www.CoreJ2ME.ru
* You may use/modify for any non-commercial purpose
*-------------------------------------------------*/  

class AnimateTimerTask extends TimerTask
{
  private AnimationCanvas canvas;
  
  public AnimateTimerTask(AnimationCanvas canvas)
  {
    this.canvas = canvas;
  }
  /*--------------------------------------------------
  * Determine next location of the ball.
  * If the ball reaches any edge, change the color
  * Track how many times we"ve switced directions
  *-------------------------------------------------*/  
  public final void run()
  {
    // If past the right edge or prior to left edge...
    if ((canvas.x_loc + canvas.radius +  canvas.x_dir > canvas.getWidth()) ||
        (canvas.x_loc - canvas.radius +  canvas.x_dir < 0))
    {                
      canvas.x_dir = -canvas.x_dir;
      canvas.changeColor();
      canvas.directionChanged++;
    }
    // If past the bottom or before the top...
    if ((canvas.y_loc + canvas.radius +  canvas.y_dir > canvas.getHeight()) ||
        (canvas.y_loc - canvas.radius + canvas.y_dir < 0))      
    {          
      canvas.y_dir = -canvas.y_dir;
      canvas.changeColor();
      canvas.directionChanged++;      
    }
    // Update the new x and y locations
    canvas.x_loc += canvas.x_dir;
    canvas.y_loc += canvas.y_dir;
 
    canvas.repaint();            
  }
}
/*--------------------------------------------------
* AnimationCanvas.java
*
* Show a ball that bounces around on a canvas 
* Each time we hit a wall we change the ball color
* We also clear the screen after "n" number of
* hits against the wall
* The "left" and "right" keys change the ball size
* The "Fire" key resets the display, however,
* it leaves the ball size the same.
*
* Example from the book:     Core J2ME Technology
* Copyright John W. Muchow   http://www.CoreJ2ME.ru
* You may use/modify for any non-commercial purpose
*-------------------------------------------------*/
class AnimationCanvas extends Canvas implements CommandListener
{
  private Animation midlet;         // Main midlet
  private Command cmExit;          // Exit midlet
  private int    keyFire,           // Reset ball            
                 keyRight,          // Increase ball radius
                 keyLeft;           // Decrease ball radius
  private boolean clearBackground = false; // Clear background
  private Random random;            // Random number
  int x_loc,                        // Current x & y locations
      y_loc,                        
      radius,                       // Ball radius
      red,                          // rgb colors
      green,                       
      blue,                       
      x_dir,                // Next x & y positions of ball
      y_dir,                
      start_x,              // Where ball starts 
      start_y,
      directionChanged = 0; // How many times we"ve hit a wall
  private static final int MAX_CHANGES = 50;
  /*--------------------------------------------------
  * Constructor
  *-------------------------------------------------*/
  public AnimationCanvas(Animation midlet)
  {
    // Save reference to main midlet
    this.midlet = midlet;
    random = new java.util.Random();
    
    // Determine starting location and direction of ball
    init();
    radius = 7;
    
    // Create exit command and "Fire" key
    cmExit = new Command("Exit", Command.EXIT, 1);
    keyFire =  getKeyCode(FIRE);
    keyRight = getKeyCode(RIGHT);
    keyLeft = getKeyCode(LEFT);
    
    addCommand(cmExit);
    setCommandListener(this);
  } 
  /*--------------------------------------------------
  * Paint a new ball, clearing the screen as asked
  *-------------------------------------------------*/
  protected void paint(Graphics g)
  {
    // Max edge hits, reset everything
    if (directionChanged > MAX_CHANGES)
      init();
    
    // Clear the background
    if (clearBackground)
    {
      g.setColor(255, 255, 255);
      g.fillRect(0, 0, getWidth(), getHeight());
      clearBackground = !clearBackground;
    }
    
    // Set color and draw another ball
    g.setColor(red, green, blue);                    
    g.fillArc( x_loc, y_loc, radius, radius, 0, 360);
  }
  /*--------------------------------------------------
  * Initialize starting location and direction of ball
  *-------------------------------------------------*/
  private void init()
  {
    // Start close to the middle
    x_loc = getWidth() / 2;
    y_loc = getHeight() / 2;      
    // The direction the ball is heading
    x_dir = (random.nextInt() % 10);
    if (x_dir == 0)  x_dir = 1;
    
    y_dir = (random.nextInt() % 10);
    if (y_dir == 0)  y_dir = 1;
    directionChanged = 0;    
    clearBackground = true;      
    changeColor();      
  }
  
  /*--------------------------------------------------
  * Change the colors.
  * I am counting on the implemention to substitute
  * a use-able color if any of these are out of the 
  * devices supported range
  *-------------------------------------------------*/
  protected void changeColor()
  {
    // The shift is to remove any sign (negative) bit    
    red = (random.nextInt() >>> 1) % 256;
    green = (random.nextInt() >>> 1) % 256;
    blue = (random.nextInt() >>> 1) % 256;
  }
  /*--------------------------------------------------
  * Event handling
  *-------------------------------------------------*/  
  public void commandAction(Command c, Displayable d)
  {
    if (c == cmExit)
      midlet.exitMIDlet();
  }
  /*--------------------------------------------------
  * Reset ball is "Fire" key is pressed
  *-------------------------------------------------*/  
  protected void keyPressed(int keyCode)
  {
    // Restart
    if (keyCode == keyFire)
      init();
    // Decrease ball size
    else if (keyCode == keyLeft)
      radius = Math.max(1, --radius);
    else if (keyCode == keyRight)
    // Increase ball size
     radius = Math.min(getWidth() / 4, ++radius);
  }
}





Timer and animation

/* License
 * 
 * Copyright 1994-2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  
 *  * Redistribution of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 * 
 *  * Redistribution in binary form must reproduce the above copyright notice,
 *      this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of Sun Microsystems, Inc. or the names of contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *  
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *  
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility. 
 */
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;
public class TimerDemo extends MIDlet {
  Display    display;
  StarField  field = new StarField();
  FieldMover mover = new FieldMover();
  Timer      timer = new Timer();
  public TimerDemo() {
    display = Display.getDisplay( this );
  }
  protected void destroyApp( boolean unconditional ) { }
  protected void startApp() {
    display.setCurrent( field );
    timer.schedule( mover, 100, 100 );
  }
  protected void pauseApp() { }
  public void exit(){
    timer.cancel(); // stop scrolling
    destroyApp( true );
    notifyDestroyed();
  }
class FieldMover extends TimerTask {
  public void run(){
    field.scroll();
  }
}
class StarField extends Canvas {
  int        height;
  int        width;
  int[]      stars;
  Random     generator = new Random();
  boolean    painting = false;
  public StarField(){
    height      = getHeight();
    width       = getWidth();
    stars       = new int[ height ];
    for( int i = 0; i < height; ++i ){
      stars[i] = -1;
    }
  }
  public void scroll() {
    if( painting ) return;
    for( int i = height-1; i > 0; --i ){
      stars[i] = stars[i-1];
    }
    stars[0] = ( generator.nextInt() % ( 3 * width ) ) / 2;
    if( stars[0] >= width ){
      stars[0] = -1;
    }
    repaint();
  }
  protected void paint( Graphics g ){
    painting = true;
    g.setColor( 0, 0, 0 );
    g.fillRect( 0, 0, width, height );
    g.setColor( 255, 255, 255 );
    for( int y = 0; y < height; ++y ){
      int x = stars[y];
      if( x == -1 ) continue;
      g.drawLine( x, y, x, y );
    }
    painting = false;
  }
  protected void keyPressed( int keyCode ){
    exit();
  }
}
}





Timer and sticker

/*
 * TimerMIDlet.java Copyright (c) 2000 Sun Microsystems, Inc. All Rights
 * Reserved.
 * 
 * Author: Srikanth Raju
 * 
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information"). You shall not disclose such
 * Confidential Information and shall use it only in accordance with the terms
 * of the license agreement you entered into with Sun.
 * 
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
 * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
import java.util.Timer;
import java.util.TimerTask;
import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.ChoiceGroup;
import javax.microedition.lcdui.rumand;
import javax.microedition.lcdui.rumandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.Ticker;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
public class TimerMIDlet extends MIDlet implements CommandListener {
  private Display display = null;
  private Ticker stockTicker = null;
  private String[] _stocks = { "SUNW", "ORCL", "NOK", "MOT" };
  private String[] _prices = { "9", "11", "9.25", "14.5" };
  private static final Command exitCommand = new Command("Exit",
      Command.STOP, 0);
  private static final Command backCommand = new Command("Back",
      Command.BACK, 0);
  private static final Command doneCommand = new Command("Done", Command.OK,
      0);
  private Timer stockRefresh = null;
  private StockRefreshTask stockRefreshTask = null;
  private int refresh_interval = 10000; // 1000 = 1 second
  private List menu = null;
  private ChoiceGroup _updatesChoices = null;
  private Form _updatesForm = null;
  private String _currentMenu = null;
  public TimerMIDlet() {
  }
  public void startApp() throws MIDletStateChangeException {
    display = Display.getDisplay(this);
    menu = new List("Stock Menu", Choice.IMPLICIT);
    menu.append("Updates", null);
    menu.append("Add Stocks", null);
    menu.append("Remove Stocks", null);
    menu.addCommand(exitCommand);
    menu.setCommandListener(this);
    // Make the ticker
    stockTicker = new Ticker(makeTickerString());
    menu.setTicker(stockTicker);
    display.setCurrent(menu);
    _currentMenu = "Stock Menu";
    _updatesForm = new Form("Updates");
    _updatesChoices = new ChoiceGroup("Update Interval:", Choice.EXCLUSIVE);
    _updatesChoices.append("Continuous", null); // will be 10 seconds
    _updatesChoices.append("15 minutes", null);
    _updatesChoices.append("30 minutes", null);
    _updatesChoices.append("1 hour", null);
    _updatesChoices.append("3 hours", null);
    _updatesForm.setTicker(stockTicker);
    _updatesForm.append(_updatesChoices);
    _updatesForm.addCommand(backCommand);
    _updatesForm.addCommand(doneCommand);
    _updatesForm.setCommandListener(this);
    //Set up and start the timer to refresh the stock quotes
    stockRefreshTask = new StockRefreshTask();
    stockRefresh = new Timer();
    stockRefresh.schedule(stockRefreshTask, 0, refresh_interval);
  }
  public String makeTickerString() {
    String retString = new String();
    for (int i = 0; i < _stocks.length; i++) {
      retString += _stocks[i];
      retString += " @ ";
      retString += _prices[i];
      retString += " ";
    }
    return retString;
  }
  public void pauseApp() {
    // free memory used by these objects
    display = null;
    stockRefresh = null;
    stockRefreshTask = null;
  }
  public void destroyApp(boolean unconditional)
      throws MIDletStateChangeException {
    notifyDestroyed();
  }
  public void commandAction(Command c, Displayable d) {
    if (c == exitCommand) {
      try {
        destroyApp(false);
      } catch (MIDletStateChangeException msce) {
        System.out.println("Error in detroyApp(false) ");
        msce.printStackTrace();
      }
      notifyDestroyed();
    } else if (c == backCommand) {
      _currentMenu = "Stock Menu";
      display.setCurrent(menu);
    } else if (c == doneCommand) {
      switch (_updatesChoices.getSelectedIndex()) {
      case 0:
        refresh_interval = 10000;
        break;
      case 1:
        refresh_interval = 900000;
        break;
      case 2:
        refresh_interval = 1800000;
        break;
      case 3:
        refresh_interval = 3600000;
        break;
      case 4:
        refresh_interval = 10800000;
        break;
      default:
        break;
      }
      stockRefreshTask.cancel();
      stockRefreshTask = new StockRefreshTask();
      stockRefresh.schedule(stockRefreshTask, 0, refresh_interval);
      display.setCurrent(menu);
      _currentMenu = "Stock Menu";
    } else {
      List shown = (List) display.getCurrent();
      switch (shown.getSelectedIndex()) {
      case 0: // Updates
        display.setCurrent(_updatesForm);
        _currentMenu = "Updates";
        break;
      case 1: // Add Stock
        System.out.println("Add Stock... ");
        _currentMenu = "Add Stock";
        break;
      case 2: // Remove Stock
        System.out.println("Remove Stock... ");
        _currentMenu = "Remove Stock";
        break;
      }
    }
  }
  /**
   * This is an extention of the TimerTask class which runs when called by
   * Timer. It refreshes the stock info for each stock from the quote server
   * and checks to see if any of the alerts should be fired.
   */
  class StockRefreshTask extends TimerTask {
    /**
     * Execute the Timer"s Task
     */
    public void run() {
      try {
        // Just return if the database is empty
        if (_stocks.length == 0)
          return;
        else {
          for (int i = 0; i < _stocks.length; i++) {
            System.out.println("Stock price for symbol: "
                + _stocks[i] + " is: " + _prices[i]);
          }
        }
      } catch (Exception e) {
        System.out.println("error("UPDATE FAILED", 2000)");
      }
    }
  }
}





Timer Template

/*--------------------------------------------------
* TimerTemplate.java
*
* Test all six Timer scheduling options
*
* Example from the book:     Core J2ME Technology
* Copyright John W. Muchow   http://www.CoreJ2ME.ru
* You may use/modify for any non-commercial purpose
*-------------------------------------------------*/
import java.util.*;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class TimerTemplate extends MIDlet implements CommandListener
{
  private Display display;      // Our display
  private Form fmMain;         // Main form
  private Command cmExit;      // Exit midlet
  private Command cmStop;      // Stop the timer
  private Timer tm;          // Timer
  private TestTimerTask tt;       // Task
  private int count = 0;        // How many times has task run
  public TimerTemplate()
  {
    display = Display.getDisplay(this);
    // Create main form
    fmMain = new Form("Timer Test");
    fmMain.append("waiting...\n");
    // Create commands and add to form
    cmExit = new Command("Exit", Command.EXIT, 1);
    cmStop= new Command("Stop", Command.STOP, 2);
    fmMain.addCommand(cmExit);
    fmMain.addCommand(cmStop);
    fmMain.setCommandListener(this);
    
    //------------------------------------------------------
    // Option #1 - One time task with delayed start
    // Create a timer that will go off in 5 seconds
    //------------------------------------------------------
    tm = new Timer();
    tt = new TestTimerTask();
    tm.schedule(tt,5000);    
    //------------------------------------------------------
    // Option #2 - Fixed-delay repeating task with delayed start
    // Create a timer that will go off in 5 seconds
    // Repeating every 3 seconds
    //------------------------------------------------------
//    tm = new Timer();
//    tt = new TestTimerTask();
//    tm.schedule(tt,5000, 3000);    
    //------------------------------------------------------
    // Option #3 - Fixed-rate repeating task with delayed start
    // Create a timer that will go off in 5 seconds
    // Repeating every 3 seconds
    //------------------------------------------------------
//    timer = new Timer();
//    tt = new TestTimerTask();
//    timer.scheduleAtFixedRate(tt,5000, 3000);    
    //------------------------------------------------------
    // Option #4 - One time task at specified date
    // Create timer that starts at current date
    //------------------------------------------------------
//    timer = new Timer();
//    tt = new TestTimerTask();
//    timer.schedule(tt, new Date());    
    //------------------------------------------------------
    // Option #5 - Fixed-delay repeating task starting 
    //             at a specified date
    // Create timer that starts at current date
    // Repeating every 3 seconds
    //------------------------------------------------------
//    timer = new Timer();
//    tt = new TestTimerTask();
//    timer.schedule(tt, new Date(), 3000);    
    //------------------------------------------------------
    // Option #6 - Fixed-rate repeating task starting
    //             at a specified date
    // Create timer that starts at current date
    // Repeating every 3 seconds
    //------------------------------------------------------
//    timer = new Timer();
//    tt = new TestTimerTask();
//    timer.scheduleAtFixedRate(tt, new Date(), 3000);    
  }
  /*--------------------------------------------------
  * Show the main Form
  *-------------------------------------------------*/
  public void startApp ()
  {
    display.setCurrent(fmMain);
  }
  /*--------------------------------------------------
  * Shutting down. Cleanup all we created
  *-------------------------------------------------*/
  public void destroyApp (boolean unconditional)
  {
  }
  /*--------------------------------------------------
  * No pause code necessary
  *-------------------------------------------------*/
  public void pauseApp ()
  { }
  /*--------------------------------------------------
  * Process events for the main form
  *-------------------------------------------------*/
  public void commandAction(Command c, Displayable d)
  {
    if (c == cmStop)
    {
      tm.cancel();
    }
    else if (c == cmExit)
    {
      destroyApp(false);
      notifyDestroyed();
    }
  }
  /*--------------------------------------------------
  * TestTimerTask Class - Run the task
  *-------------------------------------------------*/  
  private class TestTimerTask extends TimerTask
  {
    public final void run()
    {
      fmMain.append("run count: " + ++count + "\n");
    }
  }
}