Mega Code Archive

 
Categories / Java / J2ME
 

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.Command; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Form; import javax.microedition.lcdui.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.compareTo(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();   } }