AnimatedTimer.java Source code

Java tutorial

Introduction

Here is the source code for AnimatedTimer.java

Source

/*--------------------------------------------------
 * AnimatedTimer - Main midlet.
 * Shows canvas with an animated timer. Includes
 * configuration options to start/stop the timer
 * and to adjust the sleep interval of the thread
 *
 * Example from the book:     Core J2ME Technology
 * Copyright John W. Muchow   http://www.CoreJ2ME.com
 * You may use/modify for any non-commercial purpose 
 *-------------------------------------------------*/

import java.util.Stack;

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.List;
import javax.microedition.midlet.MIDlet;

public class AnimatedTimer extends MIDlet {
    private Display display; // The display

    protected TimerCanvas cvTimer; // Canvas to display timer

    protected OptionsList lsOptions; // List to change timer options

    protected SleepForm fmSleep; // Form with gauge to set timer sleep

    protected DisplayManager displayMgr; // Class to help manage screens

    public AnimatedTimer() {
        display = Display.getDisplay(this);
        cvTimer = new TimerCanvas(this);
        lsOptions = new OptionsList("Timer options", List.IMPLICIT, this);
        fmSleep = new SleepForm("Adjust sleep", this);

        // Create a display manager object
        displayMgr = new DisplayManager(display, cvTimer);
    }

    protected void startApp() {
        // Start with the canvas
        display.setCurrent(cvTimer);
    }

    protected void pauseApp() {
    }

    protected void destroyApp(boolean unconditional) {
    }

    public void exitMIDlet() {
        destroyApp(true);
        notifyDestroyed();
    }
}

/*--------------------------------------------------
 * Use a stack to push and pop displayable objects
 *
 * public void pushDisplayable(Displayable)
 * public void popDisplayable()
 * public void home()  
 *
 * Example from the book:     Core J2ME Technology
 * Copyright John W. Muchow   http://www.CoreJ2ME.com
 * You may use/modify for any non-commercial purpose 
 *-------------------------------------------------*/

class DisplayManager extends Stack {
    private Display display; // Reference to Display object

    private Displayable mainDisplayable; // Main displayable for MIDlet

    private Alert alStackError; // Alert for error conditions

    /*--------------------------------------------------
     * Display manager constructor
     *-------------------------------------------------*/
    public DisplayManager(Display display, Displayable mainDisplayable) {
        // Only one display object per midlet, this is it
        this.display = display;
        this.mainDisplayable = mainDisplayable;

        // Create an alert displayed when an error occurs
        alStackError = new Alert("Displayable Stack Error");
        alStackError.setTimeout(Alert.FOREVER); // Modal
    }

    /*--------------------------------------------------
     * Push the current displayable onto stack and set
     * the passed in displayable as active
     *-------------------------------------------------*/
    public void pushDisplayable(Displayable newDisplayable) {
        push(display.getCurrent());
        display.setCurrent(newDisplayable);
    }

    /*--------------------------------------------------
     * Return to the main displayable object of MIDlet
     *-------------------------------------------------*/
    public void home() {
        while (elementCount > 1)
            pop();
        display.setCurrent(mainDisplayable);
    }

    /*--------------------------------------------------
     * Pop displayable from stack and set as active
     *-------------------------------------------------*/
    public void popDisplayable() {
        // If the stack is not empty, pop next displayable
        if (empty() == false)
            display.setCurrent((Displayable) pop());
        else
            // On error show an alert
            // Once acknowledged, set 'mainDisplayable' as active
            display.setCurrent(alStackError, mainDisplayable);
    }
}

/*--------------------------------------------------
 * Class TimerCanvas
 *
 * Animate a sequence of images to simulate 
 * a moving timer
 *
 * Example from the book:     Core J2ME Technology
 * Copyright John W. Muchow   http://www.CoreJ2ME.com
 * You may use/modify for any non-commercial purpose 
 *-------------------------------------------------*/

class TimerCanvas extends Canvas implements Runnable, CommandListener {
    private AnimatedTimer midlet; // Main midlet

    private Command cmExit; // Exit midlet

    private Command cmOptions; // Display options list

    private Image im = null; // Sequence of images

    private int imageCount = 4; // Four images in the sequence

    private int imageWidth; // Width of one image in the sequence

    private int imageHeight; // Height of one image in the sequence

    private int imageIndex; // Current image in the sequence

    private int translate_x; // Translated x and y

    private int translate_y;

    private int viewport_x; // Location of the viewport

    private int viewport_y;

    private boolean active = false; // Timer active?

    private boolean requestedToStop = false; // Did user request to stop timer

    private int sleepTime = 400; // Current sleep time (milliseconds)

    public TimerCanvas(AnimatedTimer midlet) {
        // Call canvas constructor
        super();

        // Save reference to MIDlet so we can
        // access the display manager class
        this.midlet = midlet;

        // Create commands & listen for events
        cmExit = new Command("Exit", Command.EXIT, 1);
        cmOptions = new Command("Config", Command.SCREEN, 2);
        addCommand(cmExit);
        addCommand(cmOptions);
        setCommandListener(this);
    }

    /*--------------------------------------------------
     * Application manager is about to display canvas
     *-------------------------------------------------*/
    protected void showNotify() {
        if (im == null) {
            try {
                // Read the png from a file and get width and
                // height of one image in the sequence
                im = Image.createImage("/timer.png");
                imageHeight = im.getHeight();
                imageWidth = im.getWidth() / imageCount;

                // Get the coordinates for x/y of viewport
                // Viewport is centered on the display
                viewport_x = (getWidth() / 2) - (imageWidth / 2);
                viewport_y = (getHeight() / 2) - (imageHeight / 2);

                // Set first translated coordinate to match viewport
                translate_x = viewport_x;
                translate_y = viewport_y;
            } catch (Exception e) {
                System.err.println("Unable to read png file.");
            }

            // Begin with the first image in the sequence
            imageIndex = 0;
        }

        // If the user has not requested to stop the timer...
        if (!requestedToStop)
            active = true;

        new Thread(this).start();
    }

    /*--------------------------------------------------
     * Application manager is no longer displaying canvas
     *-------------------------------------------------*/
    protected void hideNotify() {
        active = false;
    }

    /*--------------------------------------------------
     * Draw next timer in sequence
     *-------------------------------------------------*/
    protected void paint(Graphics g) {
        if (im != null) {
            // Due to a bug in MIDP 1.0.3 we need to
            // force a clear of the display
            g.setColor(255, 255, 255); // White pen
            g.fillRect(0, 0, getWidth(), getHeight());
            g.setColor(0, 0, 0); // Black pen

            // Viewport at center of display
            g.setClip(viewport_x, viewport_y, imageWidth, imageHeight);

            // Draw image at translated coordinates
            g.drawImage(im, translate_x, translate_y, Graphics.TOP | Graphics.LEFT);
        }
    }

    /*--------------------------------------------------
     * Loop forever, translating image coordinates
     *-------------------------------------------------*/
    public void run() {
        try {
            while (active) {
                Thread.sleep(sleepTime);
                repaint();

                // Reached the last image in sequence
                if (imageIndex == imageCount - 1) {
                    // Reset translated coordinates
                    translate_x = viewport_x;
                    translate_y = viewport_y;
                } else {
                    // Translate coordinate system to the left
                    translate_x -= imageWidth;
                }

                // Which image in the sequence is next
                imageIndex = (imageIndex + 1) % imageCount;
            }
        } catch (InterruptedException e) {
        }
    }

    /*--------------------------------------------------
     * Called from the "Config" options menu
     *-------------------------------------------------*/
    public void startTimer() {
        requestedToStop = false;
        active = true;
        repaint();
    }

    /*--------------------------------------------------
     * Called from the "Config" options menu
     *-------------------------------------------------*/
    public void stopTimer() {
        requestedToStop = true;
        active = false;
        repaint();
    }

    /*--------------------------------------------------
     * Called from form/gauge to adjust sleep
     *-------------------------------------------------*/
    public void setSleep(int sleepTime) {
        this.sleepTime = sleepTime;
    }

    /*--------------------------------------------------
     * Called from form/gauge to adjust sleep
     *-------------------------------------------------*/
    public int getSleep() {
        return sleepTime;
    }

    /*--------------------------------------------------
     * Command event handling
     *-------------------------------------------------*/
    public void commandAction(Command c, Displayable s) {
        if (c == cmOptions) {
            // Push current displayable and show the options list
            midlet.displayMgr.pushDisplayable(midlet.lsOptions);
        } else if (c == cmExit) {
            midlet.exitMIDlet();
        }
    }
}

/*--------------------------------------------------
 * Class SleepForm
 *
 * Form with gauge to adjust sleep interval of timer
 *
 * Example from the book:     Core J2ME Technology
 * Copyright John W. Muchow   http://www.CoreJ2ME.com
 * You may use/modify for any non-commercial purpose 
 *-------------------------------------------------*/

class SleepForm extends Form implements CommandListener {
    private AnimatedTimer midlet; // Main midlet

    private Command cmBack, // Back to options list
            cmHome, // Go to main displayable (canvas)
            cmSave; // Save new sleep time

    private Gauge gaSleep; // Gauge to adjust sleep

    public SleepForm(String title, AnimatedTimer midlet) {
        // Call the form constructor
        super(title);

        // Save reference to MIDlet so we can
        // access the display manager class
        this.midlet = midlet;

        // Commands
        cmSave = new Command("Save", Command.SCREEN, 1);
        cmBack = new Command("Back", Command.BACK, 2);
        cmHome = new Command("Home", Command.SCREEN, 2);

        // Gauge to adjust the length of timer sleep
        gaSleep = new Gauge("Timer Sleep", true, 100, 1000);

        // Set to current sleep. Gauge holds values 0 to 100,
        // divide the current sleep (milliseconds) by 10
        gaSleep.setValue(midlet.cvTimer.getSleep() / 10);

        // Add to form and listen for events
        append(gaSleep);
        addCommand(cmSave);
        addCommand(cmBack);
        addCommand(cmHome);
        setCommandListener(this);
    }

    /*--------------------------------------------------
     * Command event handling
     *-------------------------------------------------*/
    public void commandAction(Command c, Displayable s) {
        if (c == cmSave) {
            // Gauge returns a value between 0 and 100
            // We want milliseconds, so multiply by 10
            midlet.cvTimer.setSleep(gaSleep.getValue() * 10);

            // Return to main midlet
            midlet.displayMgr.home();

        } else if (c == cmBack) {
            // Pop the last displayable off the stack
            midlet.displayMgr.popDisplayable();
        } else if (c == cmHome) {
            // Return to main midlet
            midlet.displayMgr.home();
        }
    }
}

/*--------------------------------------------------
 * Class OptionsList
 *
 * List to provide options for configuring of timer
 *
 * Example from the book:     Core J2ME Technology
 * Copyright John W. Muchow   http://www.CoreJ2ME.com
 * You may use/modify for any non-commercial purpose 
 *-------------------------------------------------*/

class OptionsList extends List implements CommandListener {
    private AnimatedTimer midlet; // Main midlet

    private Command cmBack;

    public OptionsList(String title, int listType, AnimatedTimer midlet) {
        // Call list constructor
        super(title, listType);

        // Save reference to MIDlet so we can
        // access the display manager class
        this.midlet = midlet;

        // Create the list entries
        append("Sleep interval", null);
        append("Start", null);
        append("Stop", null);

        // Create command and listen for events
        cmBack = new Command("Back", Command.BACK, 1);
        addCommand(cmBack);
        setCommandListener(this);
    }

    /*--------------------------------------------------
     * Command event handling
     *-------------------------------------------------*/
    public void commandAction(Command c, Displayable s) {
        // Event generated by the implicit list
        if (c == List.SELECT_COMMAND) {
            switch (getSelectedIndex()) {
            case 0:
                // Push current displayable and show the form
                // to adjust the timer sleep
                midlet.displayMgr.pushDisplayable(midlet.fmSleep);
                break;

            case 1:
                // Start timer and return to previous displayable
                midlet.cvTimer.startTimer();
                midlet.displayMgr.popDisplayable();
                break;

            case 2:
                // Stop timer and return to previous displayable
                midlet.cvTimer.stopTimer();
                midlet.displayMgr.popDisplayable();
                break;
            }
        } else if (c == cmBack) {
            // Return to previous displayable
            midlet.displayMgr.popDisplayable();
        }
    }
}