/*--------------------------------------------------
* 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();
}
}
|