TodoMIDlet.java Source code

Java tutorial

Introduction

Here is the source code for TodoMIDlet.java

Source

/*--------------------------------------------------
* TodoMIDlet.java
*
* The main class for todo list MIDlet
*
* 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.io.*;
import java.util.*;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.rms.*;

public class TodoMIDlet extends MIDlet implements ItemStateListener, CommandListener {
    private Display display; // Our display
    private Form fmMain; // Main Form
    private FormAdd fmAdd; // Form to add todo item
    private Command cmAdd, // Command to add todo
            cmPrefs, // Command to set preferences
            cmExit; // Command to exit
    private Vector vecTodo; // The "master" list of todo items
    private ChoiceGroup cgTodo; // Todo items (read from vecTodo)
    protected DisplayManager displayMgr; // Class to help manage screens

    //-------------------------------------------------
    // Record Stores. One for todo, one for preferences
    //-------------------------------------------------
    private RecordStore rsTodo;
    private RecordStore rsPref;
    private static final String REC_STORE_TODO = "TodoList";
    private static final String REC_STORE_PREF = "TodoPrefs";
    private boolean flagSortByPriority = false, // Sort by priority?
            flagShowPriority = true; // Show priorities ?

    //-------------------------------------------------
    // Re-use these input streams throughout the MIDlet
    //-------------------------------------------------
    // Read from a specified byte array
    private ByteArrayInputStream istrmBytes = null;
    // Read Java data types from the above byte array
    private DataInputStream istrmDataType = null;
    // If you change length of a todo entry, bump this
    byte[] recData = new byte[25];

    //-------------------------------------------------
    // Re-use these output streams throughout the MIDlet
    //-------------------------------------------------
    // Write data into an internal byte array
    ByteArrayOutputStream ostrmBytes = null;
    // Write Java data types into the above byte array
    DataOutputStream ostrmDataType = null;

    //-------------------------------------------------
    // Record Enumerator and compare class
    //-------------------------------------------------
    private RecordEnumeration e = null;
    private ComparatorInt comp = null;

    /*--------------------------------------------------
    * MIDlet constructor.
    *-------------------------------------------------*/
    public TodoMIDlet() {
        display = Display.getDisplay(this);

        // Create 'main' and 'add todo item' forms
        // Form for setting prefs is in commandAction()
        fmMain = new Form("Todo List");
        fmAdd = new FormAdd("Add Todo", this);

        // Todo list and vector
        cgTodo = new ChoiceGroup("", Choice.MULTIPLE);
        vecTodo = new Vector();

        // Commands
        cmAdd = new Command("Add Todo", Command.SCREEN, 2);
        cmPrefs = new Command("Preferences", Command.SCREEN, 3);
        cmExit = new Command("Exit", Command.EXIT, 1);

        // Add all to form and listen for events
        fmMain.addCommand(cmAdd);
        fmMain.addCommand(cmPrefs);
        fmMain.addCommand(cmExit);
        fmMain.append(cgTodo);
        fmMain.setCommandListener(this);
        fmMain.setItemStateListener(this);

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

        // Open/create the record stores
        rsTodo = openRecStore(REC_STORE_TODO);
        rsPref = openRecStore(REC_STORE_PREF);

        // Initialize the streams
        initInputStreams();
        initOutputStreams();

        // Read preferences from rms
        refreshPreferences();

        // Initialize the enumeration for todo rms
        initEnumeration();

        // Read rms into vector
        writeRMS2Vector();

        // Build the todo list (choice group)
        rebuildTodoList();
    }

    /*--------------------------------------------------
    * Show the main Form
    *-------------------------------------------------*/
    public void startApp() {
        display.setCurrent(fmMain);
    }

    /*--------------------------------------------------
    * Shutting down. Cleanup all we created
    *-------------------------------------------------*/
    public void destroyApp(boolean unconditional) {
        // Cleanup for enumerator
        if (comp != null)
            comp.compareIntClose();
        if (e != null)
            e.destroy();

        // Cleanup streams
        try {
            if (istrmDataType != null)
                istrmDataType.close();

            if (istrmBytes != null)
                istrmBytes.close();

            if (ostrmDataType != null)
                ostrmDataType.close();

            if (ostrmBytes != null)
                ostrmBytes.close();
        } catch (Exception e) {
            db(e.toString());
        }

        // Cleanup rms
        closeRecStore(rsTodo);
        closeRecStore(rsPref);
    }

    /*--------------------------------------------------
    * No pause code necessary
    *-------------------------------------------------*/
    public void pauseApp() {
    }

    /*--------------------------------------------------
    * Open input streams
    *-------------------------------------------------*/
    private void initInputStreams() {
        istrmBytes = new ByteArrayInputStream(recData);
        istrmDataType = new DataInputStream(istrmBytes);
    }

    /*--------------------------------------------------
    * Open output streams
    *-------------------------------------------------*/
    private void initOutputStreams() {
        ostrmBytes = new ByteArrayOutputStream();
        ostrmDataType = new DataOutputStream(ostrmBytes);
    }

    /*--------------------------------------------------
    * Initialize enumeration for todo rms
    *-------------------------------------------------*/
    private void initEnumeration() {
        // Are we to bother with sorting?
        if (flagSortByPriority)
            comp = new ComparatorInt();
        else
            // We must set this to null to clear out
            // any previous setting
            comp = null;

        try {
            e = rsTodo.enumerateRecords(null, comp, false);
        } catch (Exception e) {
            db(e.toString());
        }
    }

    /*--------------------------------------------------
    * Open a record store
    *-------------------------------------------------*/
    private RecordStore openRecStore(String name) {
        try {
            // Open the Record Store, creating it if necessary
            return RecordStore.openRecordStore(name, true);
        } catch (Exception e) {
            db(e.toString());
            return null;
        }
    }

    /*--------------------------------------------------
    * Close a record store
    *-------------------------------------------------*/
    private void closeRecStore(RecordStore rs) {
        try {
            rs.closeRecordStore();
        } catch (Exception e) {
            db(e.toString());
        }
    }

    /*--------------------------------------------------
    * Delete a record store
    *-------------------------------------------------*/
    private void deleteRecStore(String name) {
        try {
            RecordStore.deleteRecordStore(name);
        } catch (Exception e) {
            db(e.toString());
        }
    }

    /*--------------------------------------------------
    * Write new todo item
    *   - Write a new record into the rms
    *   - Write a new item into the vector
    *   - Recreate the vector from the rms (which will
    *     use the proper sort (using rms enumeration)
    *   - Rebuild todo list from vector
    *-------------------------------------------------*/
    protected void addTodoItem(int priority, String text) {
        try {
            // Toss any data in the internal array so writes
            // starts at beginning (of the internal array)
            ostrmBytes.reset();

            // Write priority and todo text
            ostrmDataType.writeInt(priority);
            ostrmDataType.writeUTF(text);

            // Clear any buffered data
            ostrmDataType.flush();

            // Get stream data into byte array and write record
            byte[] record = ostrmBytes.toByteArray();
            int recordId = rsTodo.addRecord(record, 0, record.length);

            // Create a new Todo item and insert it into our Vector
            TodoItem item = new TodoItem(priority, text, recordId);
            vecTodo.addElement(item);

        } catch (Exception e) {
            db(e.toString());
        }

        // Read rms into vector
        writeRMS2Vector();

        // Rebuild todo list
        rebuildTodoList();
    }

    /*--------------------------------------------------
    * Save preferences to record store
    *-------------------------------------------------*/
    protected void savePreferences(boolean sort, boolean showSort) {
        // No changes we made
        if (sort == flagSortByPriority && showSort == flagShowPriority)
            return;

        // Save the current sort status
        boolean previouslySorted = flagSortByPriority;
        boolean previouslyShowPriority = flagShowPriority;

        try {
            // Update prefs in private variables
            flagSortByPriority = sort;
            flagShowPriority = showSort;

            // Toss any data in the internal array so writes
            // starts at beginning (of the internal array)
            ostrmBytes.reset();

            // Write the sort order and keep completed flags
            ostrmDataType.writeBoolean(flagSortByPriority);
            ostrmDataType.writeBoolean(flagShowPriority);

            // Clear any buffered data
            ostrmDataType.flush();

            // Get stream data into byte array and write record
            byte[] record = ostrmBytes.toByteArray();

            // Always write preferences at first record
            // We cannot request to set the first record unless
            // the record store has contents.
            // If empty => add a record
            // If not   => overwrite the first record
            if (rsPref.getNumRecords() == 0)
                rsPref.addRecord(record, 0, record.length);
            else
                rsPref.setRecord(1, record, 0, record.length);
        } catch (Exception e) {
            db(e.toString());
        }

        // If the sort order was changed, rebuild enumeration
        if (previouslySorted != flagSortByPriority)
            initEnumeration();

        // If we are going from non-sorted to sorted
        // or changing whether or not to show priority 
        // then we must update what's currently displayed
        if ((!previouslySorted && flagSortByPriority) || (previouslyShowPriority != flagShowPriority)) {
            // Read rms into vector
            writeRMS2Vector();

            // Rebuild todo list
            rebuildTodoList();
        }
    }

    /*--------------------------------------------------
    * Read preferences from record store
    *-------------------------------------------------*/
    private void refreshPreferences() {
        try {
            // Record store is empty
            if (rsPref.getNumRecords() == 0) {
                // Write into the store the default preferences
                savePreferences(flagSortByPriority, flagShowPriority);
                return;
            }

            // Reset input back to the beginning
            istrmBytes.reset();

            // Read configuration data stored in the first record
            rsPref.getRecord(1, recData, 0);
            flagSortByPriority = istrmDataType.readBoolean();
            flagShowPriority = istrmDataType.readBoolean();

            System.out.println("Sort: " + flagSortByPriority);
            System.out.println("Show: " + flagShowPriority);
        } catch (Exception e) {
            db(e.toString());
        }
    }

    /*--------------------------------------------------
    * Create the vector from record store contents
    *-------------------------------------------------*/
    private void writeRMS2Vector() {
        // Cleanout the vector
        vecTodo.removeAllElements();

        try {
            // Rebuild enumeration for any changes
            e.rebuild();

            while (e.hasNextElement()) {
                // Reset input back to the beginning
                istrmBytes.reset();

                // Get data into the byte array
                int id = e.nextRecordId();
                rsTodo.getRecord(id, recData, 0);

                // Create a new Todo item and insert it into our Vector
                TodoItem item = new TodoItem(istrmDataType.readInt(), istrmDataType.readUTF(), id);
                vecTodo.addElement(item);
            }
        } catch (Exception e) {
            db(e.toString());
        }
    }

    /*--------------------------------------------------
    * Store the current vector contents to the rms
    *-------------------------------------------------*/
    private void writeVector2RMS() {
        try {
            byte[] record;

            for (int i = 0; i < vecTodo.size(); i++) {
                TodoItem item = (TodoItem) vecTodo.elementAt(i);

                int priority = item.getPriority();
                String text = item.getText();

                // Toss any data in the internal array so writes
                // starts at beginning (of the internal array)
                ostrmBytes.reset();

                // Write priority and todo text
                ostrmDataType.writeInt(priority);
                ostrmDataType.writeUTF(text);

                // Clear any buffered data
                ostrmDataType.flush();

                // Get stream data into byte array and write record
                record = ostrmBytes.toByteArray();
                rsTodo.addRecord(record, 0, record.length);
            }
        } catch (Exception e) {
            db(e.toString());
        }
    }

    /*--------------------------------------------------
    * Rebuild todo list (ChoiceGroup) from the Vector
    *-------------------------------------------------*/
    protected void rebuildTodoList() {
        // Clear out the ChoiceGroup.
        for (int i = cgTodo.size(); i > 0; i--)
            cgTodo.delete(i - 1);

        TodoItem item;
        int priority;
        String text;
        StringBuffer strb;

        for (int i = 0; i < vecTodo.size(); i++) {
            // Get a todo item from vector
            item = (TodoItem) vecTodo.elementAt(i);

            // Read values from todoitem class
            priority = item.getPriority();
            text = item.getText();

            // Are we are to show priority as part of the text?
            strb = new StringBuffer((flagShowPriority ? (Integer.toString(priority) + "-") : ""));
            strb.append(text);

            // Append to todo choicegroup
            cgTodo.append(strb.toString(), null);
        }
    }

    /*--------------------------------------------------
    * This method is called when a todo item has been
    * selected in the choicegroup. We treat this as
    * a request to delete the item
    *-------------------------------------------------*/
    public void itemStateChanged(Item item) {
        ChoiceGroup cg;

        // Cast the item to a ChoiceGroup type
        cg = (ChoiceGroup) item;

        // Create an array that mirrors the ChoiceGroup
        // and populate with those items selected
        boolean selected[] = new boolean[cg.size()];
        cg.getSelectedFlags(selected);

        // Unfortunately, there is no (easy) way to determine
        // which item in the choiceGroup was "clicked" to
        // initiate this event. The best we can do is look at
        // each entry and determine its current selection state

        // For each element, see if it is selected
        // If so, delete it. Once we have found a selected
        // item, we can exit - there will never be more than
        // one item selected at any time
        for (int i = 0; i < cg.size(); i++) {
            if (selected[i]) {
                // Get the record id from the todoItem 
                // and delete record from rms
                TodoItem todoitem = (TodoItem) vecTodo.elementAt(i);
                try {
                    rsTodo.deleteRecord(todoitem.getRecordId());
                } catch (Exception e) {
                    db(e.toString());
                }

                break;
            }
        }

        // Read rms into vector
        writeRMS2Vector();

        // Rebuild todo list
        rebuildTodoList();
    }

    /*--------------------------------------------------
    * Process events for the main form
    *-------------------------------------------------*/
    public void commandAction(Command c, Displayable d) {
        if (c == cmExit) {
            destroyApp(false);
            notifyDestroyed();
        } else {
            if (c == cmAdd) {
                // Reset the textfield and choicegroup
                // on the 'add todo' form
                fmAdd.tfTodo.setString("");
                fmAdd.cgPriority.setSelectedIndex(0, true);

                // Push current displayable and activate 'add todo' form
                displayMgr.pushDisplayable(fmAdd);
            } else if (c == cmPrefs) {
                boolean flags[] = { flagSortByPriority, flagShowPriority };

                // Push current displayable and show preferences form 
                // passing in current preference settings        
                displayMgr.pushDisplayable(new FormPrefs("Preferences", this, flags));
            }
        }
    }

    /*--------------------------------------------------
    * Simple message to console for debug/errors
    *-------------------------------------------------*/
    private void db(String str) {
        System.err.println("Msg: " + str);
    }
}

/*--------------------------------------------------
* 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 acknowldeged, set 'mainDisplayable' as active
            display.setCurrent(alStackError, mainDisplayable);
    }
}

/*--------------------------------------------------
* FormAdd.java
*
* Form for adding new todoitems
* Supporting class for TodoMIDlet
*
* 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 FormAdd extends Form implements CommandListener {
    private Command cmBack, cmSave;
    protected TextField tfTodo;
    protected ChoiceGroup cgPriority;
    private TodoMIDlet midlet;

    public FormAdd(String title, TodoMIDlet midlet) {
        // Call the Form constructor
        super(title);

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

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

        // Create textfield for entering todo items
        tfTodo = new TextField("Todo", null, 15, TextField.ANY);

        // Create choicegroup and append options (no images)
        cgPriority = new ChoiceGroup("Priority", Choice.EXCLUSIVE);
        cgPriority.append("Today", null);
        cgPriority.append("Tomorrow", null);
        cgPriority.append("This Week", null);
        cgPriority.append("This Month", null);
        cgPriority.append("Someday", null);

        // Add stuff to form and listen for events
        addCommand(cmSave);
        addCommand(cmBack);
        append(tfTodo);
        append(cgPriority);
        setCommandListener(this);
    }

    public void commandAction(Command c, Displayable s) {
        if (c == cmSave) {
            // Add a new todo item
            // Notice we bump priority by 1. This is because the 
            // choicegroup entries start at zero. We would like 
            // the records in the rms to store priorities starting
            // at 1. Thus, if a user requests to display priorities 
            // on the todo list, the highest priority is 1 (not zero)
            midlet.addTodoItem(cgPriority.getSelectedIndex() + 1, tfTodo.getString());
        }

        // Any other event and we go back to the main form...
        // Pop the last displayable off the stack
        midlet.displayMgr.popDisplayable();
    }
}

/*--------------------------------------------------
* ComparatorInt.java
*
* Sorts rms records based on todo item priority
* Provides compare() method for RecordEnumerator
* Supporting class for TodoMIDlet
*
* 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 ComparatorInt implements RecordComparator {
    private byte[] record = new byte[10];

    // Read from a specified byte array
    private ByteArrayInputStream strmBytes = null;

    // Read Java data types from the above byte array
    private DataInputStream strmDataType = null;

    public void compareIntClose() {
        try {
            if (strmBytes != null)
                strmBytes.close();
            if (strmDataType != null)
                strmDataType.close();
        } catch (Exception e) {
        }
    }

    public int compare(byte[] rec1, byte[] rec2) {
        int x1, x2;

        try {
            // If either record is larger than our buffer, reallocate
            int maxsize = Math.max(rec1.length, rec2.length);
            if (maxsize > record.length)
                record = new byte[maxsize];

            // Read record #1
            // We want the priority which is first "field"
            strmBytes = new ByteArrayInputStream(rec1);
            strmDataType = new DataInputStream(strmBytes);
            x1 = strmDataType.readInt();

            // Read record #2
            strmBytes = new ByteArrayInputStream(rec2);
            strmDataType = new DataInputStream(strmBytes);
            x2 = strmDataType.readInt();

            // Compare record #1 and #2
            if (x1 == x2)
                return RecordComparator.EQUIVALENT;
            else if (x1 < x2)
                return RecordComparator.PRECEDES;
            else
                return RecordComparator.FOLLOWS;

        } catch (Exception e) {
            return RecordComparator.EQUIVALENT;
        }
    }
}

/*--------------------------------------------------
* TodoItem.java
*
* Holds data/methods for a single todo item
* Supporting class for todoMIDlet
*
* 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 TodoItem {
    private int priority;
    private String text;
    private int recordId;

    public TodoItem(int priority, String text, int recordId) {
        this.priority = priority;
        this.text = text;
        this.recordId = recordId;
    }

    public int getPriority() {
        return priority;
    }

    public void setPriority(int priority) {
        this.priority = priority;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public int getRecordId() {
        return recordId;
    }
}

/*--------------------------------------------------
* FormPrefs.java
*
* Form for specifying user preferences
* Supporting class for TodoMIDlet
*
* 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 FormPrefs extends Form implements CommandListener {
    private Command cmBack, cmSave;
    private TodoMIDlet midlet;
    private ChoiceGroup cgPrefs;

    public FormPrefs(String title, TodoMIDlet midlet, boolean flags[])

    {
        // Call the Form constructor
        super(title);

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

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

        // Choicegroup for sort order and showing priority
        cgPrefs = new ChoiceGroup("Preferences", Choice.MULTIPLE);
        cgPrefs.append("Sort by Priority", null);
        cgPrefs.append("Show Priority", null);

        // Set the current status of each entry
        cgPrefs.setSelectedFlags(flags);

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

    public void commandAction(Command c, Displayable s) {
        if (c == cmSave) {
            // Save the preferences
            midlet.savePreferences(cgPrefs.isSelected(0), cgPrefs.isSelected(1));
        }

        // Any other event and we go back to the main form...
        // Pop the last displayable off the stack
        midlet.displayMgr.popDisplayable();
    }
}