Java/J2ME/Application
Содержание
Address Book MIDlet
/*
* Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
*/
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.Vector;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
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.Item;
import javax.microedition.lcdui.ItemStateListener;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.Screen;
import javax.microedition.lcdui.TextBox;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.rms.RecordComparator;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordFilter;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
/**
* This MIDlet implements a simple address book with the following
* functionality: browsing, entry, deletion, and searching (both on device and
* over the network).
*/
public class AddressBookMIDlet extends MIDlet implements CommandListener,
ItemStateListener {
private RecordStore addrBook;
private static final int FN_LEN = 10;
private static final int LN_LEN = 20;
private static final int PN_LEN = 15;
final private static int ERROR = 0;
final private static int INFO = 1;
private Display display;
private Alert alert;
private Command cmdAdd;
private Command cmdBack;
private Command cmdCancel;
private Command cmdDial;
private Command cmdExit;
private Command cmdSelect;
private Command cmdSearchNetwork;
private Command cmdSearchLocal;
private List mainScr;
private String[] mainScrChoices = { "Search", "Add New", "Browse",
"Options" };
private Form searchScr;
private TextField s_lastName;
private TextField s_firstName;
private Form entryScr;
private TextField e_lastName;
private TextField e_firstName;
private TextField e_phoneNum;
private List nameScr;
private Vector phoneNums;
private Form optionScr;
private ChoiceGroup sortChoice;
private TextBox dialScr;
private int sortOrder = 1;
/**
* Public no-argument constructor. Called by the system to instantiate our
* class. Caches reference to the display, allocate commands, and tries to
* open the address book.
*/
public AddressBookMIDlet() {
display = Display.getDisplay(this);
cmdAdd = new Command("Add", Command.OK, 1);
cmdBack = new Command("Back", Command.BACK, 2);
cmdCancel = new Command("Cancel", Command.BACK, 2);
cmdDial = new Command("Dial", Command.OK, 1);
cmdExit = new Command("Exit", Command.EXIT, 2);
cmdSelect = new Command("Select", Command.OK, 1);
cmdSearchNetwork = new Command("Network", Command.SCREEN, 4);
cmdSearchLocal = new Command("Local", Command.SCREEN, 3);
alert = new Alert("", "", null, AlertType.INFO);
alert.setTimeout(2000);
try {
addrBook = RecordStore.openRecordStore("TheAddressBook", true);
} catch (RecordStoreException e) {
addrBook = null;
}
}
/**
* Called by the system to start our MIDlet. If the open of the address book
* fails, display an alert and continue.
*
*/
protected void startApp() {
if (addrBook == null) {
displayAlert(ERROR, "Could not open address book", null);
} else {
genMainScr();
}
}
/**
* 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) {
if (addrBook != null) {
try {
addrBook.closeRecordStore();
} catch (Exception e) {
}
}
}
/**
* Display an Alert on the screen
*
* @param type
* One of the following: ERROR, INFO
* @param msg
* Message to display
* @param s
* screen to change to after displaying alert. if null, revert to
* main screen
*/
private void displayAlert(int type, String msg, Screen s) {
alert.setString(msg);
switch (type) {
case ERROR:
alert.setTitle("Error!");
alert.setType(AlertType.ERROR);
break;
case INFO:
alert.setTitle("Info");
alert.setType(AlertType.INFO);
break;
}
display.setCurrent(alert, s == null ? display.getCurrent() : s);
}
/**
* Notify the system that we are exiting.
*/
private void midletExit() {
destroyApp(false);
notifyDestroyed();
}
/**
* Create the first screen of our MIDlet. This screen is a list.
*/
private Screen genMainScr() {
if (mainScr == null) {
mainScr = new List("Menu", List.IMPLICIT, mainScrChoices, null);
mainScr.addCommand(cmdSelect);
mainScr.addCommand(cmdExit);
mainScr.setCommandListener(this);
}
display.setCurrent(mainScr);
return mainScr;
}
/**
* Sort order option screen. Allows us to set sort order to either sorting
* by last name (default), or first name.
*/
private Screen genOptionScr() {
if (optionScr == null) {
optionScr = new Form("Options");
optionScr.addCommand(cmdBack);
optionScr.setCommandListener(this);
sortChoice = new ChoiceGroup("Sort by", Choice.EXCLUSIVE);
sortChoice.append("First name", null);
sortChoice.append("Last name", null);
sortChoice.setSelectedIndex(sortOrder, true);
optionScr.append(sortChoice);
optionScr.setItemStateListener(this);
}
display.setCurrent(optionScr);
return optionScr;
}
/**
* Search screen.
*
* Displays two <code>TextField</code>s: one for first name, and one for
* last name. These are used for searching the address book.
*
* @see AddressBookMIDlet#genNameScr
*/
private Screen genSearchScr() {
if (searchScr == null) {
searchScr = new Form("Search");
searchScr.addCommand(cmdBack);
searchScr.addCommand(cmdSearchNetwork);
searchScr.addCommand(cmdSearchLocal);
searchScr.setCommandListener(this);
s_firstName = new TextField("First name:", "", FN_LEN,
TextField.ANY);
s_lastName = new TextField("Last name:", "", LN_LEN, TextField.ANY);
searchScr.append(s_firstName);
searchScr.append(s_lastName);
}
s_firstName.delete(0, s_firstName.size());
s_lastName.delete(0, s_lastName.size());
display.setCurrent(searchScr);
return searchScr;
}
/**
* Name/Phone number entry screen
*
* Displays three <code>TextField</code>s: one for first name, one for
* last name, and one for phone number. These are used to capture data to
* add to the address book.
*
* @see AddressBookMIDlet#addEntry
*/
private Screen genEntryScr() {
if (entryScr == null) {
entryScr = new Form("Add new");
entryScr.addCommand(cmdCancel);
entryScr.addCommand(cmdAdd);
entryScr.setCommandListener(this);
e_firstName = new TextField("First name:", "", FN_LEN,
TextField.ANY);
e_lastName = new TextField("Last name:", "", LN_LEN, TextField.ANY);
e_phoneNum = new TextField("Phone Number", "", PN_LEN,
TextField.PHONENUMBER);
entryScr.append(e_firstName);
entryScr.append(e_lastName);
entryScr.append(e_phoneNum);
}
e_firstName.delete(0, e_firstName.size());
e_lastName.delete(0, e_lastName.size());
e_phoneNum.delete(0, e_phoneNum.size());
display.setCurrent(entryScr);
return entryScr;
}
/**
* Generates a list of first/last/phone numbers. Can be called as a result
* of a browse command (genBrowseScr) or a search command (genSearchScr).
*
* title title of this screen (since it can be called from a browse or a
* search command. f if not null, first name to search on l if not null,
* last name to search on
*/
private Screen genNameScr(String title, String f, String l, boolean local) {
SimpleComparator sc;
SimpleFilter sf = null;
RecordEnumeration re;
phoneNums = null;
if (local) {
sc = new SimpleComparator(
sortOrder == 0 ? SimpleComparator.SORT_BY_FIRST_NAME
: SimpleComparator.SORT_BY_LAST_NAME);
if (f != null || l != null) {
sf = new SimpleFilter(f, l);
}
try {
re = addrBook.enumerateRecords(sf, sc, false);
} catch (Exception e) {
displayAlert(ERROR, "Could not create enumeration: " + e, null);
return null;
}
} else {
re = new NetworkQuery(f, l, sortOrder);
}
nameScr = null;
if (re.hasNextElement()) {
nameScr = new List(title, List.IMPLICIT);
nameScr.addCommand(cmdBack);
nameScr.addCommand(cmdDial);
nameScr.setCommandListener(this);
phoneNums = new Vector(6);
try {
while (re.hasNextElement()) {
byte[] b = re.nextRecord();
String pn = SimpleRecord.getPhoneNum(b);
nameScr.append(SimpleRecord.getFirstName(b) + " "
+ SimpleRecord.getLastName(b) + " "
+ SimpleRecord.getPhoneNum(b), null);
phoneNums.addElement(pn);
}
} catch (Exception e) {
displayAlert(ERROR, "Error while building name list: " + e,
null);
return null;
}
display.setCurrent(nameScr);
} else {
displayAlert(INFO, "No names found", null);
}
return nameScr;
}
/**
* Generate a screen with which to dial the phone. Note: this may or may not
* be implemented on a given implementation.
*/
private void genDialScr() {
dialScr = new TextBox("Dialing", (String) phoneNums.elementAt(nameScr
.getSelectedIndex()), PN_LEN, TextField.PHONENUMBER);
dialScr.addCommand(cmdCancel);
dialScr.setCommandListener(this);
display.setCurrent(dialScr);
}
/**
* Add an entry to the address book. Called after the user selects the
* addCmd while in the genEntryScr screen.
*/
private void addEntry() {
String f = e_firstName.getString();
String l = e_lastName.getString();
String p = e_phoneNum.getString();
byte[] b = SimpleRecord.createRecord(f, l, p);
try {
addrBook.addRecord(b, 0, b.length);
displayAlert(INFO, "Record added", mainScr);
} catch (RecordStoreException rse) {
displayAlert(ERROR, "Could not add record" + rse, mainScr);
}
}
/***************************************************************************
* This method implements a state machine that drives the MIDlet from one
* state (screen) to the next.
*/
public void commandAction(Command c, Displayable d) {
if (d == mainScr) {
// Handle main sceen
if (c == cmdExit) {
midletExit(); // exit
} else if ((c == List.SELECT_COMMAND) || (c == cmdSelect)) {
switch (mainScr.getSelectedIndex()) {
case 0:
// display search screen
genSearchScr();
break;
case 1:
// display name entry screen
genEntryScr();
break;
case 2:
// display all names
genNameScr("Browse", null, null, true);
break;
case 3:
// display option screen
genOptionScr();
break;
default:
displayAlert(ERROR, "Unexpected index!", mainScr);
}
}
} else if (d == nameScr) {
// Handle a screen with names displayed, either
// from a browse or a search
if (c == cmdBack) {
// display main screen
genMainScr();
} else if (c == cmdDial) {
// dial the phone screen
genDialScr();
}
} else if (d == entryScr) {
// Handle the name entry screen
if (c == cmdCancel) {
// display main screen
genMainScr();
} else if (c == cmdAdd) {
// display name entry screen
addEntry();
}
} else if (d == optionScr) {
// Handle the option screen
if (c == cmdBack) {
// display main screen
genMainScr();
}
} else if (d == searchScr) {
// Handle the search screen
if (c == cmdBack) {
// display main screen
genMainScr();
} else if (c == cmdSearchNetwork || c == cmdSearchLocal) {
// display search of local addr book
genNameScr("Search Result", s_firstName.getString(), s_lastName
.getString(), c == cmdSearchLocal);
}
} else if (d == dialScr) {
if (c == cmdCancel) {
// display main screen
genMainScr();
}
}
}
/**
* Gets called when the user is viewing the sort options in the optionScr.
* Takes the new selected index and changes the sort order (how names are
* displayed from a search or a browse).
*
* item An item list
*/
public void itemStateChanged(Item item) {
if (item == sortChoice) {
sortOrder = sortChoice.getSelectedIndex();
}
}
}
/*
* Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
*/
/*
* Class to query a network service for address book entries and parse the
* result. Uses HttpConnection to fetch the entries from a server.
*
* The http request is made using a base url provided by the caller with the
* query arguments for last name and first name encoded in the query parameters
* of the URL.
*/
class NetworkQuery implements RecordEnumeration {
private StringBuffer buffer = new StringBuffer(60);
private String[] fields = new String[3];
private String empty = new String();
private Vector results = new Vector(20);
private Enumeration resultsEnumeration;
final static String baseurl = "http://127.0.0.1:8080/Book/netaddr";
/**
* Create a RecordEnumeration from the network.
*
* Query a network service for addresses matching the specified criteria.
* The base URL of the service has the query parameters appended. The
* request is made and the contents parsed into a Vector which is used as
* the basis of the RecordEnumeration. lastname the last name to search for
* firstname the first name to search for sortorder the order in which to
* sort 1 is by last name, 0 is by first name
*/
NetworkQuery(String firstname, String lastname, int sortorder) {
HttpConnection c = null;
int ch;
InputStream is = null;
InputStreamReader reader;
String url;
// Format the complete URL to request
buffer.setLength(0);
buffer.append(baseurl);
buffer.append("?last=");
buffer.append((lastname != null) ? lastname : empty);
buffer.append("&first=");
buffer.append((firstname != null) ? firstname : empty);
buffer.append("&sort=" + sortorder);
url = buffer.toString();
// Open the connection to the service
try {
c = open(url);
results.removeAllElements();
/*
* Open the InputStream and construct a reader to convert from bytes
* to chars.
*/
is = c.openInputStream();
reader = new InputStreamReader(is);
while (true) {
int i = 0;
fields[0] = empty;
fields[1] = empty;
fields[2] = empty;
do {
buffer.setLength(0);
while ((ch = reader.read()) != -1 && (ch != ",")
&& (ch != "\n")) {
if (ch == "\r") {
continue;
}
buffer.append((char) ch);
}
if (ch == -1) {
throw new EOFException();
}
if (buffer.length() > 0) {
if (i < fields.length) {
fields[i++] = buffer.toString();
}
}
} while (ch != "\n");
if (fields[0].length() > 0) {
results.addElement(SimpleRecord.createRecord(fields[0],
fields[1], fields[2]));
}
}
} catch (Exception e) {
} finally {
try {
if (is != null) {
is.close();
}
if (c != null) {
c.close();
}
} catch (Exception e) {
}
}
resultsEnumeration = results.elements();
}
/**
* Read the HTTP headers and the data using HttpConnection. Check the
* response code to ensure successful open.
*
* Connector.open is used to open url and a HttpConnection is returned. The
* HTTP headers are read and processed. url the URL to open throws
* IOException for any network related exception
*/
private HttpConnection open(String url) throws IOException {
HttpConnection c;
int status = -1;
// Open the connection and check for redirects
while (true) {
c = (HttpConnection) Connector.open(url);
// Get the status code,
// causing the connection to be made
status = c.getResponseCode();
if ((status == HttpConnection.HTTP_TEMP_REDIRECT)
|| (status == HttpConnection.HTTP_MOVED_TEMP)
|| (status == HttpConnection.HTTP_MOVED_PERM)) {
// Get the new location and close the connection
url = c.getHeaderField("location");
c.close();
} else {
break;
}
}
// Only HTTP_OK (200) means the content is returned.
if (status != HttpConnection.HTTP_OK) {
c.close();
throw new IOException("Response status not OK");
}
return c;
}
/**
* Returns true if more elements exist in enumeration.
*/
public boolean hasNextElement() {
return resultsEnumeration.hasMoreElements();
}
/**
* Returns a copy of the next record in this enumeration,
*/
public byte[] nextRecord() {
return (byte[]) resultsEnumeration.nextElement();
}
/**
* The following are simply stubs that we don"t implement...
*/
public boolean hasPreviousElement() {
return false;
}
public void destroy() {
}
public boolean isKeptUpdated() {
return false;
}
public void keepUpdated(boolean b) {
return;
}
public int nextRecordId() {
return 0;
}
public int numRecords() {
return 0;
}
public byte[] previousRecord() {
return null;
}
public int previousRecordId() {
return 0;
}
public void rebuild() {
return;
}
public void reset() {
return;
}
}
/*
* Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
*/
/**
* This class implements the RecordFilter interface. It works on the records
* created by SimpleRecord. It filters on first name and/or last name.
*/
class SimpleFilter implements RecordFilter {
// first and last names on which to filter
private String first;
private String last;
/**
* Public constructor: stores the first and last names on which to filter.
* Stores first/last names as lower case so that filters are are
* case-insensitive.
*/
public SimpleFilter(String f, String l) {
first = f.toLowerCase();
last = l.toLowerCase();
}
/**
* Takes a record, (r), and checks to see if it matches the first and last
* name set in our constructor.
*
* Extracts the first and last names from the record, converts them to lower
* case, then compares them with the values extracted from the record.
*
* return true if record matches, false otherwise
*/
public boolean matches(byte[] r) {
String f = SimpleRecord.getFirstName(r).toLowerCase();
String l = SimpleRecord.getLastName(r).toLowerCase();
return f.startsWith(first) && l.startsWith(last);
}
}
/**
* This class implements the RecordComparator interface. It works on the records
* created by SimpleRecord. It sorts on either first name or last name.
*/
class SimpleComparator implements RecordComparator {
/**
* Sorting values (sort by first or last name)
*/
public final static int SORT_BY_FIRST_NAME = 1;
public final static int SORT_BY_LAST_NAME = 2;
/**
* Sort order. Set by constructor.
*/
private int sortOrder = -1;
/**
* Public constructor: sets the sort order to be used for this
* instantiation.
*
* Sanitize s: if it is not one of the valid sort codes, set it to
* SORT_BY_LAST_NAME silently. s the desired sort order
*/
SimpleComparator(int s) {
switch (s) {
case SORT_BY_FIRST_NAME:
case SORT_BY_LAST_NAME:
this.sortOrder = s;
break;
default:
this.sortOrder = SORT_BY_LAST_NAME;
break;
}
}
/**
* This is the compare method. It takes two records, and depending on the
* sort order extracts and lexicographically compares the subfields as two
* Strings.
*
* r1 First record to compare r2 Second record to compare return one of the
* following:
*
* RecordComparator.PRECEDES if r1 is lexicographically less than r2
* RecordComparator.FOLLOWS if r1 is lexicographically greater than r2
* RecordComparator.EQUIVALENT if r1 and r2 are lexicographically equivalent
*/
public int compare(byte[] r1, byte[] r2) {
String n1 = null;
String n2 = null;
// Based on sortOrder, extract the correct fields
// from the record and convert them to lower case
// so that we can perform a case-insensitive compare.
if (sortOrder == SORT_BY_FIRST_NAME) {
n1 = SimpleRecord.getFirstName(r1).toLowerCase();
n2 = SimpleRecord.getFirstName(r2).toLowerCase();
} else if (sortOrder == SORT_BY_LAST_NAME) {
n1 = SimpleRecord.getLastName(r1).toLowerCase();
n2 = SimpleRecord.getLastName(r2).toLowerCase();
}
int n = n1.rupareTo(n2);
if (n < 0) {
return RecordComparator.PRECEDES;
}
if (n > 0) {
return RecordComparator.FOLLOWS;
}
return RecordComparator.EQUIVALENT;
}
}
/*
* Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
*/
/**
* This class provides static methods that allow us to hide the format of a
* record. N.B. no synchronized access is provided
*/
final class SimpleRecord {
private final static int FIRST_NAME_INDEX = 0;
private final static int LAST_NAME_INDEX = 20;
private final static int FIELD_LEN = 20;
private final static int PHONE_INDEX = 40;
private final static int MAX_REC_LEN = 60;
private static StringBuffer recBuf = new StringBuffer(MAX_REC_LEN);
// Don"t let anyone instantiate this class
private SimpleRecord() {
}
// Clear internal buffer
private static void clearBuf() {
for (int i = 0; i < MAX_REC_LEN; i++) {
recBuf.insert(i, " ");
}
recBuf.setLength(MAX_REC_LEN);
}
/**
* Takes component parts and return a record suitable for our address book.
*
* return byte[] the newly created record first record field: first name
* last record field: last name num record field: phone number
*/
public static byte[] createRecord(String first, String last, String num) {
clearBuf();
recBuf.insert(FIRST_NAME_INDEX, first);
recBuf.insert(LAST_NAME_INDEX, last);
recBuf.insert(PHONE_INDEX, num);
recBuf.setLength(MAX_REC_LEN);
return recBuf.toString().getBytes();
}
/**
* Extracts the first name field from a record. return String contains the
* first name field b the record to parse
*/
public static String getFirstName(byte[] b) {
return new String(b, FIRST_NAME_INDEX, FIELD_LEN).trim();
}
/**
* Extracts the last name field from a record. return String contains the
* last name field b the record to parse
*/
public static String getLastName(byte[] b) {
return new String(b, LAST_NAME_INDEX, FIELD_LEN).trim();
}
/**
* Extracts the phone number field from a record. return String contains the
* phone number field b the record to parse
*/
public static String getPhoneNum(byte[] b) {
return new String(b, PHONE_INDEX, FIELD_LEN).trim();
}
}
A simple Photo and Animation Album
/*
* @(#)PhotoAlbum.java 1.6 01/04/04
*
* Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
*
* 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.io.IOException;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.Vector;
/**
* The PhotoAlbum MIDlet class provides the commands and screens
* that implement a simple Photo and Animation Album.
* The images and animations to be displayed are configured
* in the descriptor file with attributes.
* <p>
* It provides simple options to vary the speed of display
* and the picture frames used.
*
*/
public class PhotoAlbum extends MIDlet
implements CommandListener, ItemStateListener
{
private Display display; // The display for this MIDlet
private PhotoFrame frame; // The Frame and Canvas for images
private ChoiceGroup borderChoice; // List of border choices
private ChoiceGroup speedChoice; // List of speed choices
private Form optionsForm; // The form holding the options
private Alert alert; // The Alert used for errors
private Vector imageNames; // Strings with the image names
private List imageList; // List of Image titles
private Command exitCommand; // The exit command
private Command okCommand; // The ok command
private Command optionsCommand; // The command to edit options
private Command backCommand; // The command to go back
/**
* Construct a new PhotoAlbum MIDlet and initialize the base
* options and main PhotoFrame to be used when the MIDlet is
* started.
*/
public PhotoAlbum() {
display = Display.getDisplay(this);
exitCommand = new Command("Exit", Command.EXIT, 1);
optionsCommand = new Command("Options", Command.SCREEN, 1);
okCommand = new Command("Ok", Command.OK, 3);
backCommand = new Command("Back", Command.SCREEN, 3);
frame = new PhotoFrame();
frame.setStyle(2);
frame.setSpeed(2);
frame.addCommand(optionsCommand);
frame.addCommand(backCommand);
frame.setCommandListener(this);
alert = new Alert("Warning");
setupImages();
}
/**
* Start up the MIDlet by setting the display
* to show the image name list.
*/
protected void startApp() {
display.setCurrent(imageList);
}
/**
* Pausing is easy since there are no background activities
* or record stores that need to be closed.
*/
protected void pauseApp() {
frame.reset(); // Discard images cached in the frame
}
/**
* Destroy must cleanup everything not handled by the garbage
* collector.
*/
protected void destroyApp(boolean unconditional) {
frame.reset(); // Discard images cached in the frame
}
/**
* Respond to commands, including exit.
* On the exit command, cleanup and notify that
* the MIDlet has been destroyed.
*/
public void commandAction(Command c, Displayable s) {
if (c == exitCommand) {
destroyApp(false);
notifyDestroyed();
} else if (c == optionsCommand) {
display.setCurrent(genOptions());
} else if (c == okCommand && s == optionsForm) {
display.setCurrent(frame);
} else if (c == List.SELECT_COMMAND) {
int i = imageList.getSelectedIndex();
String image = (String)imageNames.elementAt(i);
try {
frame.setImage(image);
display.setCurrent(frame);
} catch (java.io.IOException e) {
alert.setString("Unable to locate " + image);
display.setCurrent(alert, imageList);
}
} else if (c == backCommand) {
display.setCurrent(imageList);
}
}
/**
* Listener for changes to options.
*/
public void itemStateChanged(Item item) {
if (item == borderChoice) {
frame.setStyle(borderChoice.getSelectedIndex());
} else if (item == speedChoice) {
frame.setSpeed(speedChoice.getSelectedIndex());
}
}
/**
* Generate the options form with speed and style choices.
* Speed choices are stop, slow, medium, and fast.
* Style choices for borders are none, plain, fancy.
*/
private Screen genOptions() {
if (optionsForm == null) {
optionsForm = new Form("Options");
optionsForm.addCommand(okCommand);
optionsForm.setCommandListener(this);
optionsForm.setItemStateListener(this);
speedChoice = new ChoiceGroup("Speed",
Choice.EXCLUSIVE);
speedChoice.append("Stop", null);
speedChoice.append("Slow", null);
speedChoice.append("Medium", null);
speedChoice.append("Fast", null);
speedChoice.setSelectedIndex(2, true);
optionsForm.append(speedChoice);
borderChoice = new ChoiceGroup("Borders",
Choice.EXCLUSIVE);
borderChoice.append("None", null);
borderChoice.append("Plain", null);
borderChoice.append("Fancy", null);
borderChoice.setSelectedIndex(2, true);
optionsForm.append(borderChoice);
}
return optionsForm;
}
/**
* Check the attributes in the Descriptor that identify
* images and titles and initialize the lists imageNames
* and imageList.
*/
private void setupImages() {
imageNames = new Vector();
imageList = new List("Images", List.IMPLICIT);
imageList.addCommand(exitCommand);
imageList.setCommandListener(this);
for (int n = 1; n < 100; n++) {
String nthImage = "PhotoImage-"+ n;
String image = getAppProperty(nthImage);
if (image == null || image.length() == 0)
break;
String nthTitle = "PhotoTitle-" + n;
String title = getAppProperty(nthTitle);
if (title == null || title.length() == 0)
title = image;
imageNames.addElement(image);
imageList.append(title, null);
}
}
}
/*
* @(#)Animation.java 1.5 01/04/04
*
* Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
*
* 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.
*/
/**
* An Animation contains the set of images to display.
* Images are read from resource files supplied in the
* JAR file.
* <p>
* This implementation keeps the Images in the heap.
* If memory is short, a more deliberate management
* of Image may be required.
*/
class Animation {
/**
* Location to draw the animation, set these fields to
* change the location where the image is drawn.
*/
int x, y;
/**
* The width and the height of the images (max of all if they
* are different).
* They are set when images are loaded and should not be changed.
*/
int width, height;
/**
* Vector of images in the sequence.
*/
private Vector images;
/**
* Current index into the sequence of images.
*/
private int index;
/**
* Size of sequence of images.
* Set to a large number until the last image of
* the sequence has been read.
*/
private int size;
/**
* Prefix or name of the image.
*/
private String prefix;
/**
* Create a new Animation.
*/
Animation() {
images = new Vector(30);
}
/**
* Advance to the next image.
* If the number of images is known then just advance
* and wrap around if necessary.
* If the number of images is not known then when
* advancing off the end of the known images try to
* create a new image using the pattern.
* When an attempt fails that sets the number of images.
*/
void next() {
int nextindex = index + 1;
if (nextindex >= size) {
index = 0;
} else if (nextindex >= images.size()) {
// Try to read the next image
// If that works put it into the images vector
try {
String name = prefix + nextindex + ".png";
Image image = Image.createImage(name);
images.setSize(nextindex+1);
images.setElementAt(image, nextindex);
index = nextindex;
} catch (IOException ex) {
// No more images, set the size of the sequence.
size = nextindex;
index = 0;
} catch (Exception e) {
size = nextindex;
index = 0;
}
} else {
// Index is within range of Images already read
index = nextindex;
}
}
/**
* Back up to the previous image.
* Wrap around to the end if at the beginning.
*/
void previous() {
index--;
if (index < 0) {
index = images.size()-1;
}
}
/**
* Paint the current image in the sequence.
* The image is drawn to the target graphics context
* at the x, and y of the Animation.
* @param g graphics context to which the next image is drawn.
*/
public void paint(Graphics g) {
if (images.size() > 0) {
g.drawImage((Image)images.elementAt(index), x, y, 0);
}
}
/**
* Load Images from resource files using
* <code>Image.createImage</code>.
* The first image is loaded to determine whether it is a
* single image or a sequence of images and to make sure it exists.
* Subsequent images are loaded on demand when they are needed.
* If the name given is the complete name of the image then
* it is a singleton.
* Otherwise it is assumed to be a sequence of images
* with the name as a prefix. Sequence numbers (n) are
* 0, 1, 2, 3, .... The full resource name is the concatenation
* of name + n + ".png".
* <p>
* Subsequent images are loaded when they are needed. See
* <code>next</code> and <code>previous</code> for details.
* @param name the name or prefix of the resource image names
* @exception IOException is thrown if the image or the first
* of the sequence cannot be found.
* @exception OutOfMemoryError if no memory can be allocated for
* the image.
*/
void loadImage(String prefix) throws IOException {
this.prefix = prefix;
Image image = null;
images.setSize(0);
index = 0;
try {
// Try the name supplied for the single image case.
// If it is found then do the setup and return
image = Image.createImage(prefix);
size = 1;
} catch (IOException ex) {
// Use the prefix + "0.png" to locate the first of
// a series of images.
String name = prefix + "0.png";
image = Image.createImage(name);
size = 999999999;
}
width = image.getWidth();
height = image.getHeight();
images.addElement(image);
}
/**
* Reset the Animation to reduce memory usage.
* Discard all but the first image.
*/
void reset() {
if (images.size() > 0) {
for (int i = 0; i < images.size(); i++)
images.setElementAt(null, i);
}
}
}
/*
* @(#)PhotoFrame.java 1.6 01/04/04
* Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
*
* 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.
*/
/**
* This class provides the picture frame and drives the animation
* of the frames and the picture. It handles the starting and stopping
* of the Animation and contains the Thread used to do
* the timing and requests that the Canvas is repainted
* periodically. Additionally, it has controls for border
* style and animation speed.
*/
class PhotoFrame extends Canvas implements Runnable {
private Animation animation; // The Animation sequencer.
private int speed; // Animation speed set
private Thread thread; // Thread used for triggering repaints
private long paintTime; // Time of most recent paint
private Image image; // Buffer image of screen
private Image bimage; // Pattern used for border
private int style; // Border style
/*
* Mapping of speed values to delays in milliseconds.
* Indices map to those in the ChoiceGroup.
*/
private final static int speeds[] = {999999999, 500, 250, 100};
/**
* Create a new PhotoFrame. Creates an offscreen mutable
* image into which the border is drawn.
* Border style is none (0).
* Speed is stopped (0) until set.
*/
PhotoFrame() {
animation = new Animation();
image = Image.createImage(getWidth(), getHeight());
setStyle(0);
setSpeed(0);
}
/**
* Load a new photo into the frame.
* Load the images into the Animation and pick
* where the image should be placed on the canvas.
* Also draw the frame into the buffered image based
* on the animation size.
* If the images can"t be loaded, just reset the origin
* and throw an IOException.
* @param name the prefix of the resource to load.
* @throws IOException when no images can be loaded.
*/
void setImage(String prefix) throws IOException {
try {
animation.loadImage(prefix);
animation.x = (getWidth() - animation.width) / 2;
animation.y = (getHeight() - animation.height) / 2;
paintFrame(style, animation.x, animation.y,
animation.width, animation.height);
} catch (java.io.IOException ex) {
// No image to display just show an empty frame.
animation.x = 0;
animation.y = 0;
paintFrame(style, 10, 10,
getWidth()-20, getHeight()-20);
throw ex;
}
}
/**
* Reset the PhotoFrame so it holds minimal resources
* by resetting the animation.
* The animation thread is stopped.
*/
void reset() {
animation.reset();
image = null;
thread = null;
}
/**
* Handle key events. FIRE events toggle between
* running and stopped. LEFT and RIGHT key events
* when stopped show the previous or next image.
*/
protected void keyPressed(int keyCode) {
int action = getGameAction(keyCode);
switch (action) {
case RIGHT:
if (thread == null) {
animation.next();
repaint();
}
break;
case LEFT:
if (thread == null) {
animation.previous();
repaint();
}
break;
case FIRE:
// Use FIRE to toggle the activity of the thread
if (thread == null) {
thread = new Thread(this);
thread.start();
} else {
synchronized (this) {
// Wake up the thread
this.notify();
thread = null;
}
}
break;
}
}
/**
* Handle key repeat events as regular key events.
*/
protected void keyRepeated(int keyCode) {
keyPressed(keyCode);
}
/**
* Set the animation speed.
* @param speed speedo of animation 0-3;
* 0 == stop; 1 = slow, 2 = medium, 3 = fast.
*/
void setSpeed(int speed) {
this.speed = speed;
}
/**
* Set the frame style.
* Recreate the photo frame image from the current animation
* and the new style.
*/
void setStyle(int style) {
this.style = style;
paintFrame(style, animation.x, animation.y,
animation.width, animation.height);
}
/**
* Notified when Canvas is made visible.
* Create the thread to run the animation timing.
*/
protected void showNotify() {
thread = new Thread(this);
thread.start();
}
/**
* Notified when the Canvas is no longer visible.
* Signal the running Thread that it should stop.
*/
protected void hideNotify() {
thread = null;
}
/**
* Paint is called whenever the canvas should be redrawn.
* It clears the canvas and draws the frame and the
* current frame from the animation.
* @param g the Graphics context to which to draw
*/
protected void paint(Graphics g) {
paintTime = System.currentTimeMillis();
if (image != null) {
// Draw the frame unless only the picture is being
// re-drawn.
// This is the inverse of the usual clip check.
int cx = 0, cy = 0, cw = 0, ch = 0;
if ((cx = g.getClipX()) < animation.x ||
(cy = g.getClipY()) < animation.y ||
((cx + (cw = g.getClipWidth())) >
(animation.x + animation.width)) ||
((cy + (ch = g.getClipHeight())) >
(animation.y + animation.height))) {
g.drawImage(image, 0, 0,
Graphics.LEFT|Graphics.TOP);
}
// Draw the image if it intersects the clipping region
if (intersectsClip(g, animation.x, animation.y,
animation.width, animation.height)) {
animation.paint(g);
}
}
}
/**
* Return true if the specified rectangle does intersect the
* clipping rectangle of the graphics object. If it returns true
* then the object must be drawn otherwise it would be clipped
* completely.
* The checks are done in a order with early exits to make this
* as inexpensive as possible.
* @param g the Graphics context to check
* @param x the upper left corner of the rectangle
* @param y the upper left corner of the rectangle
* @param w the width of the rectangle
* @param h the height of the rectangle
* @return true if the rectangle intersects the clipping region
*/
boolean intersectsClip(Graphics g, int x, int y, int w, int h) {
int cx = g.getClipX();
if (x + w <= cx)
return false;
int cw = g.getClipWidth();
if (x > cx + cw)
return false;
int cy = g.getClipY();
if (y + h <= cy)
return false;
int ch = g.getClipHeight();
if (y > cy + ch)
return false;
return true;
}
/**
* Paint the photo frame into the buffered screen image.
* This will avoid drawing each of its parts on each repaint.
* Paint will only need to put the image into the frame.
* @param style the style of frame to draw.
* @param x the x offset of the image.
* @param y the y offset of the image
* @param width the width of the anmiation image
* @param height the height of the animation image
*/
private void paintFrame(int style, int x, int y,
int width, int height) {
Graphics g = image.getGraphics();
// Clear the entire canvas to white
g.setColor(0xffffff);
g.fillRect(0, 0, getWidth()+1, getHeight()+1);
// Set the origin of the image and paint the border and image.
g.translate(x, y);
paintBorder(g, style, width, height);
}
/**
* Runs the animation and makes the repaint requests.
* The thread will exit when it is no longer the current
* Animation thread.
*/
public void run() {
Thread me = Thread.currentThread();
long scheduled = System.currentTimeMillis();
paintTime = scheduled;
while (me == thread) {
synchronized (this) {
try {
// Update when the next frame should
// be drawn and compute the delta
scheduled += speeds[speed];
long delta = scheduled - paintTime;
if (delta > 0) {
this.wait(delta);
}
animation.next();
// Request a repaint only of the image
repaint(animation.x, animation.y,
animation.width,
animation.height);
} catch (InterruptedException e) {
}
}
}
}
/**
* Draw a border of the selected style.
* Style:
* <OL>
* <LI> Style 0: No border is drawn.
* <LI> Style 1: A simple border is drawn
* <LI> Style 2: The border is outlined and an image
* is created to tile within the border.
* </OL>
* @param g graphics context to which to draw.
* @param x the horizontal offset in the frame of the image.
* @param y the vertical offset in the frame
* @param w the width reserved for the image
* @param h the height reserved of the image
*/
private void paintBorder(Graphics g, int style, int w, int h) {
if (style == 1) {
g.setColor(0x808080);
g.drawRect(-1, -1, w + 1, h + 1);
g.drawRect(-2, -2, w + 3, h + 3);
}
if (style == 2) {
// Draw fancy border with image between outer
// and inner rectangles
if (bimage == null)
bimage = genBorder();
int bw = bimage.getWidth();
int bh = bimage.getHeight();
int i;
// Draw the inner and outer solid border
g.setColor(0x808080);
g.drawRect(-1, -1, w + 1, h + 1);
g.drawRect(-bw - 2, -bh - 2,
w + bw * 2 + 3, h + bh * 2 + 3);
// Draw it in each corner
g.drawImage(bimage, -1, -1,
Graphics.BOTTOM|Graphics.RIGHT);
g.drawImage(bimage, -1, h + 1,
Graphics.TOP|Graphics.RIGHT);
g.drawImage(bimage, w + 1, -1,
Graphics.BOTTOM|Graphics.LEFT);
g.drawImage(bimage, w + 1, h + 1,
Graphics.TOP|Graphics.LEFT);
// Draw the embedded image down left and right sides
for (i = ((h % bh) / 2); i < h - bh; i += bh) {
g.drawImage(bimage, -1, i,
Graphics.RIGHT|Graphics.TOP);
g.drawImage(bimage, w + 1, i,
Graphics.LEFT|Graphics.TOP);
}
// Draw the embedded image across the top and bottom
for (i = ((w % bw) / 2); i < w - bw; i += bw) {
g.drawImage(bimage, i, -1,
Graphics.LEFT|Graphics.BOTTOM);
g.drawImage(bimage, i, h + 1,
Graphics.LEFT|Graphics.TOP);
}
}
}
/**
* Create an image for the border.
* The border consists of a simple "+" drawn in a 5x5 image.
* Fill the image with white and draw the "+" as magenta.
*/
private Image genBorder() {
Image image = Image.createImage(5, 5);
Graphics g = image.getGraphics();
g.setColor(255, 255, 255);
g.fillRect(0, 0, 5, 5);
g.setColor(128, 0, 255);
g.drawLine(2, 1, 2, 3); // vertical
g.drawLine(1, 2, 3, 2); // horizontal
return image;
}
}
Personal Information Manager
/*
J2ME: The Complete Reference
James Keogh
Publisher: McGraw-Hill
ISBN 0072227109
*/
import javax.microedition.pim.*;
import java.util.Enumeration;
import java.util.Calendar;
import java.util.Date;
public class PIMTest {
public PIMTest() {
}
/**************************************************************************
* Contact/ContactList sample code
***************************************************************************/
public void createAContact() {
// Create a support contact entry in the device"s
//local address book
// so that the users have has the contact
//information for anything that they
// need help with about your application
PIM pim = PIM.getInstance();
ContactList cl = null;
Contact new_contact = null;
try {
// Open write only since you"re just going to
// add your support contact
// info to the device"s database
cl = (ContactList)
pim.openPIMList(PIM.CONTACT_LIST, PIM.WRITE_ONLY);
} catch (PIMException e) {
// failed opening the default contact list!
// Error case - abort this attempt
System.err.println(
"Error accessing database - aborting action");
return;
} catch (SecurityException e) {
// user rejected application"s request for
//write access to contact list
// This is not an error condition and can be normal
System.out.println(
"Okay, this application won"t add the contact");
return;
}
// Create an "empty" contact to work with
new_contact = cl.createContact();
// Add your company"s info: company name,
//two support phone numbers, a support
// email, and a note about what product the user has. Add whatever
// information that the native contact list
//supports and don"t add it if
// the field is not supported.
if (cl.isSupportedField(Contact.ORG))
new_contact.addString(Contact.ORG, PIMItem.ATTR_NONE,
"Acme, Inc.");
if (cl.isSupportedField(Contact.TEL)) {
new_contact.addString(Contact.TEL, PIMItem.ATTR_NONE,
"800-888-8888");
new_contact.addString(Contact.TEL, PIMItem.ATTR_NONE,
"800-888-8889");
}
if (cl.isSupportedField(Contact.EMAIL))
new_contact.addString(Contact.EMAIL,
PIMItem.ATTR_NONE, "support@acme.ru");
if (cl.isSupportedField(Contact.NOTE))
new_contact.addString(Contact.NOTE, PIMItem.ATTR_NONE,
"You"ve purchased application XXX with registration number NNN.");
try {
// commits it to the list and the native database
new_contact.rumit(); // commits it to the list and the native database
}
catch (PIMException e) {
// failed committing the contact
System.err.println(
"This application cannot add the contact info");
}
try {
cl.close();
} catch (PIMException e) {
// failed to close the list
}
}
public void retrieveContacts() {
// Get all contacts with last name starting with "S" (e.g.
// Smith, Sanders, Stargell, etc.) for a listing screen
PIM pim = PIM.getInstance();
ContactList cl = null;
Contact search_template = null;
Enumeration s_contacts = null;
try {
cl = (ContactList) pim.openPIMList(PIM.CONTACT_LIST,
PIM.READ_ONLY);
} catch (PIMException e) {
// failed opening the default contact list!
System.err.println(
"Error accessing database - aborting action");
return;
} catch (SecurityException e) {
// user rejected application"s request for
// read access to contact list
// This is not an error condition and can be normal
System.out.println(
"Okay, this application won"t get contacts");
return;
}
// first create a "template" contact which we"ll use for matching
search_template = cl.createContact();
if (cl.isSupportedArrayElement(Contact.NAME,
Contact.NAME_FAMILY)) {
// this particular contact list does contain last names, so we
// can now do the search
// now fill in the search parameters of last name
// starting with "S"
String[] name_struct = new String[Contact.NAMESIZE];
name_struct[Contact.NAME_FAMILY] = "S";
search_template.addStringArray(Contact.NAME,
PIMItem.ATTR_NONE, name_struct);
}
else if (cl.isSupportedField(Contact.FORMATTED_NAME)) {
// the contact implementation doesn"t have individual name
// fields, so try the single name field FORMATTED_NAME
search_template.addString(Contact.FORMATTED_NAME,
PIMItem.ATTR_NONE, "S");
}
try {
// Get the enumeration of matching elements
s_contacts = cl.items(search_template);
} catch (PIMException e) {
// failed to retrieve elements due to error!
System.err.println(
"This application cannot retrieve the contacts");
}
try {
cl.close();
} catch (PIMException e) {
// failed to close the list
}
}
public void modifyAContact() {
// Code sample:
// Update John Smith"s home phone number
// from "555-0000" to "555-1212"
// since he moved...
PIM pim = PIM.getInstance();
ContactList cl = null;
Enumeration contacts = null;
try {
cl = (ContactList) pim.openPIMList(
PIM.CONTACT_LIST, PIM.READ_WRITE);
} catch (PIMException e) {
// failed opening the default contact list!
System.err.println(
"This application failed to open the contact list");
} catch (SecurityException e) {
// user rejected application"s request
// for read/write access to contact list
// This is not an error condition and can be normal
System.out.println(
"Okay, this application won"t get contacts");
return;
}
// first create a "template" contact which we"ll use for matching
// to find John Smith"s contact entry
Contact template = cl.createContact();
String tel_number = "";
if (cl.isSupportedField(Contact.NAME)) {
String[] name_struct = new String[Contact.NAMESIZE];
name_struct[Contact.NAME_FAMILY] = "Smith";
name_struct[Contact.NAME_FAMILY] = "John";
template.addStringArray(
Contact.NAME, PIMItem.ATTR_NONE, name_struct);
}
if (cl.isSupportedField(Contact.TEL)) {
template.addString(Contact.TEL, Contact.ATTR_HOME, "555-0000");
}
try {
// Get the enumeration of matching elements
contacts = cl.items(template);
} catch (PIMException e) {
// failed retrieving the items enumeration due to an error
System.err.println(
"This application cannot retrieve the contact");
}
// update all John Smith entries with old home numbers of 555-0000
while (contacts!= null && contacts.hasMoreElements()) {
Contact c = (Contact) contacts.nextElement();
for (int index = c.countValues(Contact.TEL); index != 0; index--)
{
if (c.getString(Contact.TEL, index).equals("555-0000")) {
c.setString(Contact.TEL, index, Contact.ATTR_HOME,
"555-1212");
try {
// save change to the database
c.rumit();
} catch (PIMException e) {
// Oops couldn"t save the data...
System.err.println(
"This application cannot commit the contact info");
}
break; // go to next matching element
}
}
}
try {
cl.close();
} catch (PIMException e) {
// failed to close the list
}
}
public void deleteContacts() {
// Delete all contacts at company WorldCom
// since they won"t be answering
// phone calls anymore...
PIM pim = PIM.getInstance();
ContactList cl = null;
Enumeration contacts = null;
try {
cl = (ContactList) pim.openPIMList(
PIM.CONTACT_LIST, PIM.READ_WRITE);
} catch (PIMException e) {
// failed opening the default contact list!
System.err.println(
"This application failed to open the contact list");
return;
} catch (SecurityException e) {
// user rejected application"s request for
// read/write access to contact list
// This is not an error condition and can be normal
System.out.println(
"Okay, this application won"t get contacts");
return;
}
// first create a "template" contact which we"ll use for matching
// to find WorldCom contact entries
Contact template = cl.createContact();
if (cl.isSupportedField(Contact.ORG)) {
template.addString(Contact.ORG,
PIMItem.ATTR_NONE, "WorldCom");
try {
// Get the enumeration of matching elements
contacts = cl.items(template);
} catch (PIMException e) {
// failed retrieving the items enumeration due to an error
System.err.println(
"This application cannot commit the contact info");
}
}
// delete all WorldCom entries
while (contacts != null && contacts.hasMoreElements()) {
Contact c = (Contact) contacts.nextElement();
try {
cl.removeContact(c);
} catch (PIMException e) {
// couldn"t delete the entry for some
// reason (probably shredded)
System.err.println(
"This application cannot remove the contact info");
}
}
try {
cl.close();
} catch (PIMException e) {
// failed to close the list
}
}
/***************************************************************************
Event/EventList sample code
***************************************************************************/
public void createAnEvent() {
// Create an event entry in the device"s local calendar
// reminding the user to register your application
PIM pim = PIM.getInstance();
EventList el = null;
Event new_event = null;
try {
// Open write only since you"re just going to
//add your registration
// event info to the device"s database
el = (EventList) pim.openPIMList(
PIM.EVENT_LIST, PIM.WRITE_ONLY);
} catch (PIMException e) {
// failed opening the default event list!
// Error case - abort this attempt
System.err.println(
"Error accessing database - aborting action");
return;
} catch (SecurityException e) {
// user rejected application"s request
// for write access to event list
// This is not an error condition and can be normal
System.out.println(
"Okay, this application won"t add the event");
return;
}
// Create an "empty" event to work with
new_event = el.createEvent();
// Add a registration reminder event:
// make it two weeks from now with an
// alarm 10 minutes before the occurrence, and
// add a note with the phone or email to call.
if (el.isSupportedField(Event.START)) {
Date d = new Date();
long l = d.getTime() + (long)1209600000;
new_event.addDate(Event.START, PIMItem.ATTR_NONE, l);
}
if (el.isSupportedField(Event.ALARM))
new_event.addInt(Event.ALARM, PIMItem.ATTR_NONE, 600);
if (el.isSupportedField(Event.SUMMARY))
new_event.addString(Event.SUMMARY, PIMItem.ATTR_NONE,
"Register Your Product!");
if (el.isSupportedField(Event.NOTE))
new_event.addString(Event.NOTE, PIMItem.ATTR_NONE, "You"ve purchased application XXX with registration number NNN. Please register it now. Look in the Contact List for information on how to contact us.");
try {
// commits it to the list and the native database
new_event.rumit(); // commits it to the list and the native database
}
catch (PIMException e) {
// failed committing the event
System.err.println("This application cannot add the event");
}
try {
el.close();
} catch (PIMException e) {
// failed to close the list
}
}
public void retrieveEvents() {
// Get all events occurring for the coming week,
// for a listing screen
PIM pim = PIM.getInstance();
EventList el = null;
Event search_template = null;
Enumeration this_weeks_events = null;
try {
el = (EventList) pim.openPIMList(
PIM.EVENT_LIST, PIM.READ_ONLY);
} catch (PIMException e) {
// failed opening the default event list!
System.err.println(
"Error accessing database - aborting action");
return;
} catch (SecurityException e) {
// user rejected application"s request for
// read access to event list
// This is not an error condition and can be normal
System.out.println("Okay, this application won"t get events");
return;
}
// calculcate today"s date and next week"s date
long current_time = (new Date()).getTime();
long next_week = current_time + 604800000;
try {
// Get the enumeration of matching elements
this_weeks_events = el.items(
EventList.OCCURRING, current_time, next_week, true);
} catch (PIMException e) {
// failed to retrieve elements due to error!
// Error case - abort this attempt
System.err.println(
"This application cannot retrieve the events");
}
try {
el.close();
} catch (PIMException e) {
// failed to close the list
}
}
public void modifyEvents() {
// Code sample:
// Postpone all events from today until
// tomorrow (sick day today...)
PIM pim = PIM.getInstance();
EventList el = null;
Enumeration todays_events = null;
try {
el = (EventList) pim.openPIMList(
PIM.EVENT_LIST, PIM.READ_WRITE);
} catch (PIMException e) {
// failed opening the default event list!
System.err.println(
"Error accessing database - aborting action");
return;
} catch (SecurityException e) {
// user rejected application"s requestfor read/write access to contact list
// This is not an error condition and can be normal
System.out.println("Okay, this application won"t modify any event");
return;
}
// calculate today"s start and end times
Calendar start_of_day = Calendar.getInstance();
// start of work day is 7am
start_of_day.set(Calendar.HOUR_OF_DAY, 7); // start of work day is 7am
Calendar end_of_day = Calendar.getInstance();
// end of work day is 8pm
end_of_day.set(Calendar.HOUR_OF_DAY, 20); // end of work day is 8pm
try {
// Get the enumeration of matching elements
todays_events = el.items(Event.OCCURRING, start_of_day.getTime().getTime(), end_of_day.getTime().getTime(), true);
} catch (PIMException e) {
// failed to retrieve elements due to error!
System.err.println(
"This application cannot retrieve the events");
}
// update all events by one day
while (todays_events != null && todays_events.hasMoreElements()) {
Event e = (Event) todays_events.nextElement();
e.setDate(Event.START, 0, PIMItem.ATTR_NONE,
e.getDate(Event.START, 0) + 86400000);
try {
// save change to the database
e.rumit();
} catch (PIMException exe) {
// Oops couldn"t save the data...
System.err.println(
"This application cannot commit the event");
}
}
try {
el.close();
} catch (PIMException e) {
// failed to close the list
}
}
public void deleteEvents() {
// Delete all events having to do with Christmas (bah humbug!)
PIM pim = PIM.getInstance();
EventList el = null;
Enumeration xmas_events = null;
try {
el = (EventList) pim.openPIMList(
PIM.EVENT_LIST, PIM.READ_WRITE);
} catch (PIMException e) {
// failed opening the default event list!
System.err.println(
"Error accessing database - aborting action");
return;
} catch (SecurityException e) {
// user rejected application"s request
// for read/write access to event list
// This is not an error condition and can be normal
System.out.println(
"Okay, this application won"t modify any event");
return;
}
try {
// Get the enumeration of matching elements
xmas_events = el.items("Christmas");
} catch (PIMException e) {
// failed retrieving the items enumeration due to an error
System.err.println(
"This application cannot retrieve the events");
return;
}
// delete all event entries containing Christmas
while (xmas_events != null && xmas_events.hasMoreElements()) {
Event e = (Event) xmas_events.nextElement();
try {
el.removeEvent(e);
} catch (PIMException exe) {
// couldn"t delete the entry for some reason
System.err.println(
"This application cannot remove the event info");
}
}
try {
el.close();
} catch (PIMException e) {
// failed to close the list
}
}
/**************************************************************************
* ToDo/ToDoList sample code
***************************************************************************/
public void createAToDo() {
// Create a todo entry in the device"s local todo list
// reminding the user to register your application
PIM pim = PIM.getInstance();
ToDoList tl = null;
ToDo new_todo = null;
try {
// Open write only since you"re just going to
// add your registration
// todo info to the device"s todo database
tl = (ToDoList) pim.openPIMList(PIM.TODO_LIST, PIM.WRITE_ONLY);
} catch (PIMException e) {
// failed opening the default todo list!
System.err.println(
"Error accessing database - aborting action");
return;
} catch (SecurityException e) {
// user rejected application"s request
// for write access to todo list
// This is not an error condition and can be normal
System.out.println(
"Okay, this application won"t add the todo");
return;
}
// Create an "empty" todo to work with
new_todo = tl.createToDo();
// Add a registration todo: make it have a
// due date of two weeks from now
// with a low priority, and
// add a note with the phone or email to call.
if (tl.isSupportedField(ToDo.DUE)) {
Date d = new Date();
long l = d.getTime() + (long)1209600000;
new_todo.addDate(ToDo.DUE, PIMItem.ATTR_NONE, l);
}
if (tl.isSupportedField(ToDo.PRIORITY))
new_todo.addInt(ToDo.PRIORITY, PIMItem.ATTR_NONE, 5);
if (tl.isSupportedField(ToDo.SUMMARY))
new_todo.addString(ToDo.SUMMARY, PIMItem.ATTR_NONE,
"Register Your Product!");
if (tl.isSupportedField(ToDo.NOTE))
new_todo.addString(ToDo.NOTE, PIMItem.ATTR_NONE,
"You"ve purchased application XXX with registration number NNN. Please register it now.Look in the Contact List for information on how to contact us.");
try {
// commits it to the list and the native database
new_todo.rumit(); // commits it to the list and the native database
}
catch (PIMException e) {
// failed committing the todo
System.err.println("This application cannot add the todo");
}
try {
tl.close();
} catch (PIMException e) {
// failed to close the list
}
}
public void retrieveToDos() {
// Get all todos due today, for a listing screen
PIM pim = PIM.getInstance();
ToDoList tl = null;
ToDo search_template = null;
Enumeration todos = null;
try {
tl = (ToDoList) pim.openPIMList(PIM.TODO_LIST, PIM.READ_ONLY);
} catch (PIMException e) {
// failed opening the default todo list!
System.err.println(
"Error accessing database - aborting action");
return;
} catch (SecurityException e) {
// user rejected application"s request for
// read access to todo list
// This is not an error condition and can be normal
System.out.println("Okay, this application won"t get todo items");
return;
}
// calculate today"s start and end times
Calendar start_of_day = Calendar.getInstance();
start_of_day.set(Calendar.HOUR_OF_DAY, 0);
Calendar end_of_day = Calendar.getInstance();
end_of_day.set(Calendar.HOUR_OF_DAY, 24);
try {
// Get the enumeration of matching elements
todos = tl.items(
ToDo.DUE, start_of_day.getTime().getTime(),
end_of_day.getTime().getTime());
} catch (PIMException e) {
// failed to retrieve elements due to error!
// Error case - abort this attempt
System.err.println(
"This application cannot retrieve the todos");
}
try {
tl.close();
} catch (PIMException e) {
// failed to close the list
}
}
public void modifyToDos() {
// Mark all stuff from yesterday as completed
PIM pim = PIM.getInstance();
ToDoList tl = null;
ToDo search_template = null;
Enumeration todos = null;
try {
tl = (ToDoList) pim.openPIMList(PIM.TODO_LIST, PIM.READ_ONLY);
} catch (PIMException e) {
// failed opening the default todo list!
System.err.println(
"Error accessing database - aborting action");
return;
} catch (SecurityException e) {
// user rejected application"s request for
// read access to todo list
// This is not an error condition and can be normal
System.out.println("Okay, this application won"t get todo items");
return;
}
// calculate today"s start and end times
Calendar start_of_day = Calendar.getInstance();
start_of_day.set(Calendar.HOUR_OF_DAY, 0);
Calendar end_of_day = Calendar.getInstance();
end_of_day.set(Calendar.HOUR_OF_DAY, 24);
try {
// Get the enumeration of matching elements
todos = tl.items(
ToDo.DUE, start_of_day.getTime().getTime() - 86400000,
end_of_day.getTime().getTime() - 86400000);
} catch (PIMException e) {
// failed to retrieve elements due to error!
// Error case - abort this attempt
System.err.println("This application cannot retrieve the todos");
}
// set all todos due yesterday to completed
//with updated completion date
while (todos != null && todos.hasMoreElements()) {
ToDo t = (ToDo) todos.nextElement();
if (tl.isSupportedField(ToDo.ruPLETED))
t.setBoolean(ToDo.ruPLETED, 0, PIMItem.ATTR_NONE, true);
if (tl.isSupportedField(ToDo.ruPLETION_DATE))
t.setDate(ToDo.ruPLETION_DATE, 0,
PIMItem.ATTR_NONE, (new Date()).getTime());
try {
// save change to the database
t.rumit();
} catch (PIMException exe) {
// Oops couldn"t save the data...
System.err.println(
"This application cannot commit the todo");
}
}
try {
tl.close();
} catch (PIMException e) {
// failed to close the list
}
}
public void deleteToDos() {
// Delete all ToDos having to do with cleaning (hired a maid instead)
PIM pim = PIM.getInstance();
ToDoList tl = null;
Enumeration todos = null;
try {
tl = (ToDoList) pim.openPIMList(PIM.TODO_LIST, PIM.READ_WRITE);
} catch (PIMException e) {
// failed opening the default todo list!
System.err.println(
"Error accessing database - aborting action");
return;
} catch (SecurityException e) {
// user rejected application"s request
// for read/write access to todo list
// This is not an error condition and can be normal
System.out.println(
"Okay, this application won"t modify any todo");
return;
}
try {
// Get the enumeration of matching elements
todos = tl.items("clean");
} catch (PIMException e) {
// failed retrieving the items enumeration due to an error
System.err.println(
"This application cannot retrieve the todos");
return;
}
// delete all event entries containing "clean"
while (todos != null && todos.hasMoreElements()) {
ToDo t = (ToDo) todos.nextElement();
try {
tl.removeToDo(t);
} catch (PIMException exe) {
// couldn"t delete the entry for some reason
System.err.println(
"This application cannot remove the todo info");
}
}
try {
tl.close();
} catch (PIMException e) {
// failed to close the list
}
}
}
Populate Address Book
/*
* Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
*/
import javax.microedition.midlet.*;
import javax.microedition.rms.*;
/**
* Class to create an example address book and populate it
* with some records
*/
public class PopulateAddressBook extends MIDlet {
private final String personalNames[] = {
"John", "Zach", "2225556669",
"Mark", "Lynn", "5125551212",
"Joy", "Beth", "2705551234",
"Abby", "Lynn", "4085558566",
"Ted", "Alan", "4125552235",
"Sterling", "Wincle", "9995559111",
"Deborah", "Elaine", "4445552323",
"Suzanne", "Melissa","5125556064",
"Frank", "Kenneth", "7775551212",
"Dwight", "Poe", "1115557234",
"Laura", "Beth", "2055558888",
"Lisa", "Dawn", "2705551267",
"Betty", "June", "5555551556",
"Yvonne", "Poe", "6665558888",
"Lizzy", "Loo", "5025557971",
"John", "Gerald", "3335551256",
"Mark", "VandenBrink", "1115557878",
"Roger", "Riggs", "3335550000",
"Antero", "Taivalsaari", "9995552222"
};
public PopulateAddressBook() {
RecordStore rs;
RecordEnumeration re;
CreateAddressBook.createRecordStore("TheAddressBook",
personalNames);
try {
rs = RecordStore.openRecordStore("TheAddressBook", true);
re = rs.enumerateRecords(null, null, false);
while (re.hasNextElement()) {
byte[] b = re.nextRecord();
System.out.println("<" + SimpleRecord.getFirstName(b) + ">" +
"<" + SimpleRecord.getLastName(b) + ">" +
"<" + SimpleRecord.getPhoneNum(b) + ">");
}
} catch (Exception e) {
System.out.println("Houston, we have a problem: " + e);
}
}
/**
* Called by the system to start our MIDlet.
*/
protected void startApp() {
destroyApp(false);
notifyDestroyed();
}
/**
* 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) {}
}
/*
* Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
*/
/**
* Static helper class that creates a record
* store from data in an array.
*/
class CreateAddressBook {
// Don"t allow this class to be instantiated
private CreateAddressBook() {}
/**
* Helper method that creates a record
* store from data in an array.
* Returns:
* true if RMS store was created
* false otherwise
* name the name of the record store to create
* seedData an array w/ data to seed record store
*/
static boolean createRecordStore(String name,
String[] seedData) {
RecordStore recordStore;
boolean ret = false;
// Delete any previous record store with same name.
// Silently ignore failure.
try {
RecordStore.deleteRecordStore(name);
} catch (Exception rse) {}
// Create new RMS store. If we fail, return false.
try {
recordStore = RecordStore.openRecordStore(name, true);
} catch (RecordStoreException rse) {
return ret;
}
ret = true; // assume success
// Now, populate the record store with the seed data
for (int i = 0; i < seedData.length; i += 3) {
byte[] record = SimpleRecord.createRecord(seedData[i],
seedData[i+1],
seedData[i+2]);
try {
recordStore.addRecord(record, 0, record.length);
} catch (RecordStoreException rse) {
ret = false;
break;
}
}
// Get here when adds are complete, or an error occured.
// In any case, close the record store. We shouldn"t
// have a failure, so silently ignore any exceptions.
try {
recordStore.closeRecordStore();
} catch (RecordStoreException rsne) {}
return ret;
}
}
/*
* Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
*/
/**
* This class implements the RecordComparator interface.
* It works on the records created by SimpleRecord.
* It sorts on either first name or last name.
*/
class SimpleComparator implements RecordComparator {
/**
* Sorting values (sort by first or last name)
*/
public final static int SORT_BY_FIRST_NAME = 1;
public final static int SORT_BY_LAST_NAME = 2;
/**
* Sort order. Set by constructor.
*/
private int sortOrder = -1;
/**
* Public constructor: sets the sort order to be
* used for this instantiation.
*
* Sanitize s: if it is not one of the
* valid sort codes, set it to SORT_BY_LAST_NAME
* silently.
* s the desired sort order
*/
SimpleComparator(int s) {
switch (s) {
case SORT_BY_FIRST_NAME:
case SORT_BY_LAST_NAME:
this.sortOrder = s;
break;
default:
this.sortOrder = SORT_BY_LAST_NAME;
break;
}
}
/**
* This is the compare method. It takes two
* records, and depending on the sort order
* extracts and lexicographically compares the
* subfields as two Strings.
*
* r1 First record to compare
* r2 Second record to compare
* return one of the following:
*
* RecordComparator.PRECEDES
* if r1 is lexicographically less than r2
* RecordComparator.FOLLOWS
* if r1 is lexicographically greater than r2
* RecordComparator.EQUIVALENT
* if r1 and r2 are lexicographically equivalent
*/
public int compare(byte[] r1,
byte[] r2) {
String n1 = null;
String n2 = null;
// Based on sortOrder, extract the correct fields
// from the record and convert them to lower case
// so that we can perform a case-insensitive compare.
if (sortOrder == SORT_BY_FIRST_NAME) {
n1 = SimpleRecord.getFirstName(r1).toLowerCase();
n2 = SimpleRecord.getFirstName(r2).toLowerCase();
} else if (sortOrder == SORT_BY_LAST_NAME) {
n1 = SimpleRecord.getLastName(r1).toLowerCase();
n2 = SimpleRecord.getLastName(r2).toLowerCase();
}
int n = n1.rupareTo(n2);
if (n < 0) {
return RecordComparator.PRECEDES;
}
if (n > 0) {
return RecordComparator.FOLLOWS;
}
return RecordComparator.EQUIVALENT;
}
}
/*
* Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
*/
/**
* This class provides static methods that allow us
* to hide the format of a record.
* N.B. no synchronized access is provided
*/
final class SimpleRecord {
private final static int FIRST_NAME_INDEX = 0;
private final static int LAST_NAME_INDEX = 20;
private final static int FIELD_LEN = 20;
private final static int PHONE_INDEX = 40;
private final static int MAX_REC_LEN = 60;
private static StringBuffer recBuf =
new StringBuffer(MAX_REC_LEN);
// Don"t let anyone instantiate this class
private SimpleRecord() {}
// Clear internal buffer
private static void clearBuf() {
for (int i = 0; i < MAX_REC_LEN; i++) {
recBuf.insert(i, " ");
}
recBuf.setLength(MAX_REC_LEN);
}
/**
* Takes component parts and return a record suitable
* for our address book.
*
* return byte[] the newly created record
* first record field: first name
* last record field: last name
* num record field: phone number
*/
public static byte[] createRecord(String first,
String last,
String num) {
clearBuf();
recBuf.insert(FIRST_NAME_INDEX, first);
recBuf.insert(LAST_NAME_INDEX, last);
recBuf.insert(PHONE_INDEX, num);
recBuf.setLength(MAX_REC_LEN);
return recBuf.toString().getBytes();
}
/**
* Extracts the first name field from a record.
* return String contains the first name field
* b the record to parse
*/
public static String getFirstName(byte[] b) {
return new String(b, FIRST_NAME_INDEX, FIELD_LEN).trim();
}
/**
* Extracts the last name field from a record.
* return String contains the last name field
* b the record to parse
*/
public static String getLastName(byte[] b) {
return new String(b, LAST_NAME_INDEX, FIELD_LEN).trim();
}
/**
* Extracts the phone number field from a record.
* return String contains the phone number field
* b the record to parse
*/
public static String getPhoneNum(byte[] b) {
return new String(b, PHONE_INDEX, FIELD_LEN).trim();
}
}
Todo MIDlet
/*--------------------------------------------------
* TodoMIDlet.java
*
* The main class for todo list MIDlet
*
* 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.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.rupareIntClose();
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.ru
* 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.ru
* 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.ru
* 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.ru
* 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.ru
* 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();
}
}
Website - J2ME Enterprise Development
Website - J2ME Enterprise Development
About MSales
The MSales application is the centerpiece of Part II in this book.
It"s used to illustrate the APIs covered earlier in the book from the
context of a real-world appliction. MSales:
* is designed for PalmOS MIDP devices but also runs on MIDP phones
* displays an Agent"s schedule, the products, customers, and complete orders
* uses HTTP to get data from the remote server and upload new order information
* it uses a JDBC accessible DB (MS Access) and servlet on the server side to store
and retrieve info
* allows user to create new order which gets sent back to the server during syncs
* authenticates users over the net