This section provides the source code for the BCExchanger. For the complete Eclipse project ZIP file, see Forum Nokia.
The example includes the following classes:
package bcexchanger; import java.util.Vector; import javax.microedition.midlet.MIDlet; import javax.microedition.midlet.MIDletStateChangeException; import bcexchanger.comm.ExchangeListener; import bcexchanger.comm.ExchangerComm; import bcexchanger.comm.ExchangerCommImpl; import bcexchanger.ui.AddressBookScreen; import bcexchanger.ui.AlertMessage; import bcexchanger.ui.MainScreen; import bcexchanger.ui.ProgressScreen; import bcexchanger.ui.Screen; import bcexchanger.ui.ServiceListScreen; /** * * This is the main application class which extends MIDlet class. It * serves as a parent to UI states (package example.BCExchanger.ui) * and listens to communicational events by implementing * ExchangeListener interface * * @see example.BCExchanger.comm.ExchangeListener */ public class BCExchangerMIDlet extends MIDlet implements ExchangeListener { private boolean commIsInitialized = false; private ExchangerComm comm; private int selectedServiceIndex; private MainScreen scr = new MainScreen(this); /** * Constructor */ public BCExchangerMIDlet() { } /** * Changes the current UI screen * * This method is used to display and make active a UI screen * * @param screen - * UI screen to display * @see example.BCExchanger.ui.Screen */ public void changeScreen(Screen screen) { screen.makeActive(); } /** * Called to force termination of MIDlet */ public void quit() { if (comm != null) { comm.cancelWaiting(); } try { destroyApp(true); notifyDestroyed(); } catch (MIDletStateChangeException e) { // Ignore, we are closing anyways } } /** * Saves the index of the service of choice * * This method is used in case several services are found during * service discovery In this case the ServiceListScreen is displayed * and on user selection this method is called to save the index of * the service of choice. * * @param index - * index of the serivice chosen by the user */ public void serviceSelected(int index) { selectedServiceIndex = index; } /** * Intitializes the communication module */ public void initComm() { if (!commIsInitialized) { // avoid double initializing comm = new ExchangerCommImpl(this); commIsInitialized = true; } } /** * Getter method for Exchanger - class representing communication * module * * @return a class implementing ExchangerComm interface, * representing communication module */ public ExchangerComm getExchanger() { return comm; } protected void destroyApp(boolean arg0) throws MIDletStateChangeException {} protected void startApp() throws MIDletStateChangeException { // ensure that we have own business card selected String uid = null; try { uid = Storage.getBCUID(); } catch (Exception e) { changeScreen(new AlertMessage(this, "Cannot use the persistent storage. Application will exit")); return; } if (uid == null) { // if the MIDlet has not been started before // and own business card has not been chosen changeScreen(new AddressBookScreen(this)); } else { changeScreen(scr); } } protected void pauseApp() {} public void onInquiryComplete(int code) { if (code == ExchangerComm.DONE) { String text = "Searching for Business Card Exchange service..."; changeScreen(new ProgressScreen(this, text)); } else if (code == ExchangerComm.ERROR) { changeScreen(new AlertMessage(this, "Errors during inquiry", scr)); } else if (code == ExchangerComm.CANCELED) { changeScreen(new AlertMessage(this, "Inquiry is canceled", scr)); } else { // unknwon error throw new RuntimeException("Internal error #26"); } } public void onServiceDiscoveryComplete(int code) { if (code == ExchangerComm.DONE) { String text = "Sending own business card..."; changeScreen(new ProgressScreen(this, text)); } else if (code == ExchangerComm.NO_RECORDS) { changeScreen(new AlertMessage( this, "Cannot find Business card exchange service on the devices", scr)); } else if (code == ExchangerComm.ERROR) { changeScreen(new AlertMessage(this, "Errors during service discovery", scr)); } else if (code == ExchangerComm.CANCELED) { changeScreen(new AlertMessage(this, "Service discovery is canceled",scr)); } else { // unknown error throw new RuntimeException("Internal error #27"); } } public synchronized int resolveMultipleServices(Vector friendlyNames) throws InterruptedException { changeScreen(new ServiceListScreen(this, friendlyNames)); wait(); // wait until the choise is made return selectedServiceIndex; } public byte[] getOwnBC() throws Exception { byte[] vCard = null; String uid; try { uid = Storage.getBCUID(); vCard = AddressBook.getVCard(uid); } catch (Exception e) { throw new Exception("getOwnBC did not succeed"); } return vCard; } public void onSendComplete(int code) { if (code == ExchangerComm.DONE) { String text = "Send was successful!"; changeScreen(new AlertMessage(this, text, scr)); } else if (code == ExchangerComm.CANCELED) { changeScreen(new AlertMessage(this, "Send is canceled ", scr)); } else { // error changeScreen(new AlertMessage(this, "Errors during sending ", scr )); } } public void onReceiveComplete(int code) { if (code == ExchangerComm.DONE) { changeScreen(new AlertMessage(this, "Receiving was successful!", scr)); } else if (code == ExchangerComm.CANCELED) { changeScreen(new AlertMessage(this, "Receiving is canceled ", scr)); } else { // error changeScreen(new AlertMessage(this, "Errors during receiving ", scr)); } } public void onReceiveBC(byte[] vCard) throws Exception { AddressBook.setVCard(vCard); } /** * This is a helper methods which unblocks waiting code in * resolveMultipleServices() */ public synchronized void unblock() { notify(); // let resolveMultipleServices complete } public void onGetComplete(int code) { } public void onPutComplete(int code) { } /** * Called in case of problems with Bluetooth OBEX server accepting connection * */ public void onServerError() { changeScreen(new AlertMessage(this, "Fatal Bluetooth error. Application will exit")); return; } }
package bcexchanger; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Enumeration; import java.util.Vector; import javax.microedition.pim.Contact; import javax.microedition.pim.ContactList; import javax.microedition.pim.PIM; import javax.microedition.pim.PIMItem; /** * * This class provides an abstraction of the address book to * application. It implements the access to the phone address book and * provides information in the form the application needs */ public class AddressBook { /** * This methods get all names from the address book and * corresponding uids. * * This method retrieves full names from the address book and * corresponding uinque identifiers (uids) * * @return two vectors: first is vector of names, second is the * vector of corresponding UIDs * @exception Exception - * in case any errors occur while working with address * book */ static public Vector[] getNamesUIDLists() throws Exception { Vector[] namesUIDs = new Vector[2]; namesUIDs[0] = new Vector(); namesUIDs[1] = new Vector(); // get the name of the address book PIM pim = PIM.getInstance(); String listNames[] = pim.listPIMLists(PIM.CONTACT_LIST); if (listNames.length == 0) { throw new RuntimeException("No available Contact lists"); } // opent the address book ContactList contactList = (ContactList) pim.openPIMList( PIM.CONTACT_LIST, PIM.READ_ONLY, listNames[0]); // veryfy the that the required list are supported if (!contactList.isSupportedField(Contact.UID)) { throw new RuntimeException( "Required field (UID) is not supported by the address book"); } // list of preferd order of the name fields Vector fieldOrder = new Vector(); fieldOrder.addElement(new Integer(Contact.FORMATTED_NAME)); // For Series40 fieldOrder.addElement(new Integer(Contact.NAME_FAMILY)); fieldOrder.addElement(new Integer(Contact.NAME_GIVEN)); fieldOrder.addElement(new Integer(Contact.NAME)); // fix Vector supportedFieldOrder = new Vector(); // create the supported list for(int i=0; i < fieldOrder.size(); i++) { Integer field = (Integer) fieldOrder.elementAt(i); if(contactList.isSupportedField(field.intValue())) { supportedFieldOrder.addElement(field); } } // in case none of the names is supported if(supportedFieldOrder.size() == 0) { throw new RuntimeException( "Required name field is not supported by the address book"); } // get the list of contacts and uids Enumeration e = contactList.items(); while (e.hasMoreElements()) { Contact contact = (Contact) e.nextElement(); try { // try to obtain any supported name String name = null; Enumeration e2 = supportedFieldOrder.elements(); while(e2.hasMoreElements()) { try { int field = ((Integer) e2.nextElement()).intValue(); if (field == Contact.NAME) { String[] nameArray = contact.getStringArray(field,0); if (nameArray != null) name = nameArray[0]; } else { name = contact.getString(field, 0); } if(name != null && name != "") { break; } else { name = null; } } catch (Exception exception) { name = null; } } // this contact does not have a suitable name, continue with the next if(name == null) { continue; } String uid = contact.getString(Contact.UID, 0); namesUIDs[0].addElement(name); namesUIDs[1].addElement(uid); } catch (IndexOutOfBoundsException ex) { // Ignore, this is a malformed contact and we'll omit it } } return namesUIDs; } /** * Get vCard of the address book entry with the particular uid * * Longer description * * @param uid - * unique identifier of the requested address book entry * @return byte array which contains a vCard * @exception Exception - * in case any error occur */ static public byte[] getVCard(String uid) throws Exception { ByteArrayOutputStream os = new ByteArrayOutputStream(); // create a search pattern (based on uid) ContactList contactList = (ContactList) PIM.getInstance() .openPIMList(PIM.CONTACT_LIST, PIM.READ_ONLY); Contact searchPattern = contactList.createContact(); searchPattern.addString(Contact.UID, PIMItem.ATTR_NONE, uid); Enumeration e = contactList.items(searchPattern); // get the first found contact if (e.hasMoreElements()) { Contact contact = (Contact) e.nextElement(); PIM.getInstance().toSerialFormat(contact, os, "UTF-8", "VCARD/2.1"); } else { throw new RuntimeException("No contacts found"); } return os.toByteArray(); } /** * Saves a vCard to the address book * * Creates a new entry based on the infromation from the vCard in * the phones address book * * @param vCard - * byte array which contains a vCard * @exception Exception - * in case any errors occur */ static public void setVCard(byte[] vCard) throws Exception { ByteArrayInputStream in = new ByteArrayInputStream(vCard); // get address book entry from the input stream PIMItem[] items = PIM.getInstance().fromSerialFormat(in, "UTF-8"); // takes the first one discards the others ContactList contactList = (ContactList) PIM.getInstance() .openPIMList(PIM.CONTACT_LIST, /* PIM.WRITE_ONLY */ PIM.READ_WRITE); Contact newContact = contactList .importContact((Contact) items[0]); newContact.commit(); } }
package bcexchanger; import javax.microedition.rms.RecordEnumeration; import javax.microedition.rms.RecordStore; /** * * This class abstracts an RMS system for storing the uid of the * contact entry from the address book, selected by user as own entry. * * @version 1.0 29.09.2005 */ public class Storage { /** * Gets the stored uid * * @return string containing uid of the the contact entry from the * address book, selected by user as own entry or null if no * contact is previously selected * @exception Exception - * in case of errors occur */ static public String getBCUID() throws Exception { String uid = null; RecordStore db = RecordStore.openRecordStore("uid", true); RecordEnumeration e = db.enumerateRecords(null, null, false); if (!e.hasNextElement()) { uid = null; } else { byte[] record = e.nextRecord(); if (record == null) { uid = null; } else { uid = new String(record); } } db.closeRecordStore(); return uid; } /** * Stores the uid * * @param uid - * string containing uid of the the contact entry from the * address book, selected by user as own entry * @exception Exception - * in case of errors occur */ static public void setBCUID(String uid) throws Exception { RecordStore db = RecordStore.openRecordStore("uid", true); // delete all records if (db.getNumRecords() != 0) { db.closeRecordStore(); RecordStore.deleteRecordStore("uid"); db = RecordStore.openRecordStore("uid", true); } byte[] record = uid.getBytes(); db.addRecord(record, 0, record.length); db.closeRecordStore(); } }
package bcexchanger.comm; import java.util.Vector; /** * * ExchangeListner is an callback interface. By means of the interface * function OBEX communication module signal to a listener about * various networking events. * * This interface is implemented by the BCExchangerMIDlet and OBEX * communication classes located in the package bcexchanger.comm use * it for signaling * * @version 1.0 27.09.2005 */ public interface ExchangeListener { /** * Called when the OBEX communication module completes the inquiry * * @param code - * inquiry completion code */ public void onInquiryComplete(int code); /** * Called when the OBEX communication module completes service * discoveries * * This method is called when service discovery on all devices is * complete * * @param * @param code - * service discovery completion code */ public void onServiceDiscoveryComplete(int code); /** * This method is called when OBEX communication module needs to * resolve services * * If several services are found, the OBEX communcation module asks * the MIDlet to resolve the services and choose the only one to * connect to * * @param friendlyNames - * list of friendly names of devices which has the service * @return index of the chosen device/service from the vector * @throws InterruptedException */ public int resolveMultipleServices(Vector friendlyNames) throws InterruptedException; /** * This method returns own business card serialized to vCard/2.1 * format * * When OBEX module needs to send the own business card to a remote * device it uses this method to request own business card from the * MIDlet * * @return byte array containing own business card serialized to * vCard/2.1 format null if there were errors or cancelation * @throws Exception */ public byte[] getOwnBC() throws Exception; /** * Called when business card send (Client OBEX PUT) is finished * * @param code - * completion code */ public void onSendComplete(int code); /** * Called when business card receive (Client OBEX GET) is finished * * @param code - * completion code */ public void onReceiveComplete(int code); /** * Called when Server OBEX GET operation is finished * * @param code - * completion code */ public void onGetComplete(int code); /** * Called when Server OBEX PUT operation is finished * * @param code - * completion code */ public void onPutComplete(int code); /** * Called when a business card from a remote device is received * * This method allows MIDlet to save received business card. * * @param vCard - * byte array containing receive business card serialized * to vCard/2.1 format * @throws Exception - * if saving of the business card fails */ public void onReceiveBC(byte[] vCard) throws Exception; /** * Called in case of problems with Bluetooth OBEX server accepting connection * */ public void onServerError(); }
package bcexchanger.comm; /** * * This is an interface to OBEX communication module. OBEX * communication module implemented by the classes from * bcexchanger.comm package realize exhange of the business card over * JSR82 OBEX. The controls of the module is abstracted in this * interface. * * BCExchanger is calling the methods of this interface implemented by * ExchangerCommImpl class to control OBEX communication. * * @version 1.0 27.09.2005 * */ public interface ExchangerComm { // Constant field declaration static final int DONE = 0; static final int CANCELED = 1; static final int ERROR = 2; static final int NO_RECORDS = 3; /** * Intitiate whole procedure of seding business card to a remote * device * * The method start the procedure of inquiry, service discovery, * tranfering own card and receiving the card from the remote * device. This method is asynchrounous and it returns immediately * after operation is started. * * @exception Exception - * if any immediate errors occur while the process is * started. In practice it is thrown when inquiry * cannot start or if this method is called while * sending process is in progress */ public void startSending() throws Exception; /** * Cancels the process of sending * * The method cancels the current operation of the sending: inquiry, * service discovery, OBEX send or receive and returns to the idle * state. * */ public void cancelSending(); /** * Starts to listen for incomming connections * * The method start listening for incomming connections. This method * is asynchronous and it returns immediately after the connection * listening is started. * */ public void startWaiting(); /** * This method stops listening for incomming connection * * The method stops listening for incomming connection if * startWaiting() was called before. Otherwise method does nothing. * */ public void cancelWaiting(); }
package bcexchanger.comm; import java.io.IOException; import javax.bluetooth.DiscoveryAgent; import javax.bluetooth.LocalDevice; import javax.microedition.io.Connection; import javax.microedition.io.Connector; import javax.obex.HeaderSet; import javax.obex.Operation; import javax.obex.ServerRequestHandler; import javax.obex.SessionNotifier; /** * * This class is the central class in the OBEX communication module * (package bcexchanger.comm). * * Class implements interface ExchangerComm and realizes the methods * for controling process of sending and receiving business cards. It * also is a parent for states of the BCExchanger communication state * machine. It keeps the current state of the state machine and * implements interface ExchangerStateParent. Using this interface * state classes access to the required functionality of the parent. * * This class waits for Bluetooth incomming connection in the separate * thread and therefore it implments Runnable interface. * * This class also works as a OBEX server. In order to serve OBEX * requests it extends the ServerRequestHandler class and overides * some of its methods. * * @version 1.0 29.09.2005 * @see bcexchanger.comm.ExchangerStateParent * bcexchanger.comm.ExchangerComm javax.obex.ServerRequestHandler * Design patterns: State * */ public class ExchangerCommImpl extends ServerRequestHandler implements ExchangerStateParent, ExchangerComm, Runnable { // Instance variables final private String uuid = "ed495afe28ed11da94d900e08161165f"; final private String serverURL = "btgoep://localhost:" + uuid; private boolean cancelWaitingInvoked = false; // becomes true if // cancelWaiting() is // called private Thread waitingThread; private ExchangeListener listener; private ExchangerState currentState; private SessionNotifier notifier = null; private Connection con = null; /** * Constructor * * description * * @param _listener - * listener of the communication module events * @exception * @see */ public ExchangerCommImpl(ExchangeListener _listener) { listener = _listener; startWaiting(); setState(new IdleState(this)); } public void setState(ExchangerState state) { currentState = state; } public ExchangeListener getListener() { return listener; } public ExchangerState getState() { return currentState; } public void startSending(int oper) throws Exception { getState().startSending(oper); } public void startSending() throws Exception { getState().startSending(0); } public void cancelSending() { getState().cancelSending(); } public void startWaiting() { cancelWaitingInvoked = false; waitingThread = new Thread(this); waitingThread.start(); } public void cancelWaiting() { cancelWaitingInvoked = true; try { notifier.close(); // indicate to acceptAndOpen that it is // canceled } catch (IOException e) { // Ignore, we're closing anyways } } public synchronized void run() { // initialize stack and make the device discoverable try { LocalDevice local = LocalDevice.getLocalDevice(); local.setDiscoverable(DiscoveryAgent.GIAC); } catch (Exception e) { // catching notifier exception listener.onServerError(); return; } try { notifier = (SessionNotifier) Connector.open(serverURL); } catch (IOException e2) { } // the cycle stops only if cancelWaiting() was called while (!cancelWaitingInvoked) { try { try { con = notifier.acceptAndOpen(this); wait(); // wait until the remote peer disconnects try { con.close(); } catch (IOException e0) { } } catch (Exception e1) { listener.onServerError(); return; } } catch (Exception e) { listener.onServerError(); return; } } } /* * This method is related to OBEX server functionality. This method * is delegating this execution to the current state * * @see javax.obex.ServerRequestHandler#onGet() */ public int onGet(Operation op) { return getState().onGet(op); } /* * This method is related to OBEX server functionality. This method * is delegating this execution to the current state * * @see javax.obex.ServerRequestHandler#onPut() */ public int onPut(Operation op) { return getState().onPut(op); } /* * This method is related to OBEX server functionality. This method * handles OBEX DISCONNECT command from the remote device. * * @see javax.obex.ServerRequestHandler#onDisconnect() */ public synchronized void onDisconnect(HeaderSet request, HeaderSet reply) { super.onDisconnect(request, reply); notify();// stops waiting in run() } public String getUUID() { return uuid; } }
package bcexchanger.comm; import javax.obex.Operation; /** * * This is the abstract base class for the all states of the * communication state machine. Each state is implementing methods * which are can be executed in that particular case. * * Classes IdleState, InquiryState, ServiceDiscoveryState, * ReceiveState and SendState inherit from this class * * @see Design patterns: State * */ public abstract class ExchangerState { protected ExchangerStateParent parent; /** * Implements OBEX GET command * * Implements server handling of the OBEX GET command * * @param op - * OBEX operation class * @return - OBEX response code * @see javax.obex.ServerRequestHandler */ abstract public int onGet(Operation op); /** * Implements OBEX PUT command * * Implements server handling of the OBEX PUT command * * @param op - * OBEX operation class * @return - OBEX response code * @see javax.obex.ServerRequestHandler */ abstract public int onPut(Operation op); /** * Implements actions related to starting sending process * * Implements reaction of the state on command to start sending * process * * @exception Exception - * if any immediate error occur * @see example.BCExchanger.comm.ExchangerComm */ abstract public void startSending(int oper) throws Exception; /** * Implements actions related to canceling sending process * * Implements reaction of the state on command to cancel sending * process * * @see example.BCExchanger.comm.ExchangerComm */ abstract public void cancelSending(); /** * Constructor * * @param - * _parent - the class which nests the current state of the * communication machine */ public ExchangerState(ExchangerStateParent _parent) { parent = _parent; } }
package bcexchanger.comm; /** * * This interface contains the methods which the states of the * communication state machine use to get needed functionality from * the parent * * This interface is implemented by ExchangerCommImpl class. * * @version 1.0 29.09.2005 * */ public interface ExchangerStateParent { /** * Current state setter * * Sets the current state in the parent * * @param state - * state of communication machine */ public void setState(ExchangerState state); /** * Listener getter * * Returns the communication event listener which is stored in the * parent * * @return communication event listener interface */ public ExchangeListener getListener(); /** * Communication state getter * * Returns current state of the communication machine which is kept * in the parent * * @return current state of the communication machine */ public ExchangerState getState(); /** * Returns "Business Card Exchanger Service" UUID * * @return string containing UUID of the Bluetooth OBEX server * connection */ public String getUUID(); }
package bcexchanger.comm; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.InputStream; import java.io.OutputStream; import javax.obex.Operation; import javax.obex.ResponseCodes; /** * * This class implements idle state of the communication state machine. In idle * state machine is waiting for external events (like incomming Bluetooth * connections or user command to start business card exchange) * * This class extends ExchangerState abstract class. * * @version 1.0 29.09.2005 * @see bcexchanger.comm.ExchangerState Design Patterns: State * */ public class IdleState extends ExchangerState { /** * Constructor * * @param _parent - * the class which is nesting the current state of the state * machine */ public IdleState(ExchangerStateParent _parent) { super(_parent); } public int onGet(Operation op) { try { OutputStream out = op.openOutputStream(); byte[] vCard = parent.getListener().getOwnBC(); // getting the // own card int vlen = vCard.length; byte[] tmpBuf = new byte[vlen + 4]; System.arraycopy(vCard, 0, tmpBuf, 4, vlen); tmpBuf[0] = (byte) ((vlen >>> 24) & 0xff); tmpBuf[1] = (byte) ((vlen >>> 16) & 0xff); tmpBuf[2] = (byte) ((vlen >>> 8) & 0xff); tmpBuf[3] = (byte) ((vlen >>> 0) & 0xff); out.write(tmpBuf); // sending data op.close(); return ResponseCodes.OBEX_HTTP_OK; } catch (Exception e) { return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; } } public int onPut(Operation op) { try { InputStream in = op.openInputStream(); byte[] fullResult = null; { byte[] buf = new byte[256]; ByteArrayOutputStream bout = new ByteArrayOutputStream(2048); for (int len = in.read(buf); len >= 0; len = in.read(buf)) bout.write(buf, 0, len); fullResult = bout.toByteArray(); } ByteArrayInputStream bin = new ByteArrayInputStream(fullResult); DataInputStream din = new DataInputStream(bin); int size = din.readInt(); byte[] vCard = new byte[size]; din.read(vCard); // card is received op.close(); parent.getListener().onReceiveBC(vCard); return ResponseCodes.OBEX_HTTP_OK; } catch (Exception e) { return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR; } } public void startSending(int oper) throws Exception { parent.setState(new InquiryState(parent, oper)); } public void cancelSending() { // Internal error, but not fatal } }
package bcexchanger.comm; import java.io.IOException; import java.util.Vector; import javax.bluetooth.DeviceClass; import javax.bluetooth.DiscoveryAgent; import javax.bluetooth.DiscoveryListener; import javax.bluetooth.LocalDevice; import javax.bluetooth.RemoteDevice; import javax.bluetooth.ServiceRecord; import javax.obex.Operation; import javax.obex.ResponseCodes; /** * * This class implements inquiry state. In inquiry state an active * device discovery is peformed. No OBEX server commands are served. * * This class extends ExchangerState abstract class. This class also * implments DiscoveryListener interface to receive Bluetooth inquiry * callbacks. * * @version 1.0 29.09.2005 * @see bcexchanger.comm.ExchangerState * javax.bluetooth.DiscoveryListener Design Patterns: State * */ public class InquiryState extends ExchangerState implements DiscoveryListener { private DiscoveryAgent agent; private Vector remoteDevices; // vector of found devices private int operation = ServiceDiscoveryState.GET; /** * Constructor * * @param _parent - * the class which is nesting the current state of the * state machine */ public InquiryState(ExchangerStateParent _parent,int oper) throws IOException { super(_parent); operation = oper; remoteDevices = new Vector(); // initiate Bluetooth LocalDevice local = LocalDevice.getLocalDevice(); agent = local.getDiscoveryAgent(); // start Bluetooth inquiry agent.startInquiry(DiscoveryAgent.GIAC, this); } /* * Inquiry state does not allow to start any other business card * exchange process. * * @see bcexchanger.comm.ExchangerState#startSending() */ public void startSending(int op) throws IOException { throw new IOException( "Inquiry is in progress. Inquiry has to be canceled before starting new sending process"); } /* * InquiryState allows to cancel inquiry process. * * @see bcexchanger.comm.ExchangerState#cancelSending() */ public void cancelSending() { agent.cancelInquiry(this); } public void deviceDiscovered(RemoteDevice dev, DeviceClass devClass) { remoteDevices.addElement(dev); } public void inquiryCompleted(int code) { try { int completionCode = ExchangerComm.ERROR; // convert the inquiry completion code to application completion // code switch (code) { case DiscoveryListener.INQUIRY_COMPLETED: completionCode = ExchangerComm.DONE; break; case DiscoveryListener.INQUIRY_TERMINATED: completionCode = ExchangerComm.CANCELED; break; case DiscoveryListener.INQUIRY_ERROR: completionCode = ExchangerComm.ERROR; break; } parent.getListener().onInquiryComplete(completionCode); // signal // that // inquiry // is // done if (code == DiscoveryListener.INQUIRY_COMPLETED) { // no errors // or // cancelations parent.setState(new ServiceDiscoveryState(parent, remoteDevices,operation)); } else { parent.setState(new IdleState(parent)); } } catch (Exception e) { parent.setState(new IdleState(parent)); } } /* * Service discovery callbacks are not handled and not supposed to * occur, since service discovery process is not started * * @see javax.bluetooth.DiscoveryListener#servicesDiscovered(int, * javax.bluetooth.ServiceRecord[]) */ public void servicesDiscovered(int arg0, ServiceRecord[] arg1) { throw new RuntimeException( "Internal error #4: InquiryState.servicesDiscovered() should not be called"); } /* * Service discovery callbacks are not handled and not supposed to * occur, since service discovery process is not started * * @see javax.bluetooth.DiscoveryListener#serviceSearchCompleted(int, * int) */ public void serviceSearchCompleted(int arg0, int arg1) { throw new RuntimeException( "Internal error #5: InquiryState.serviceSearchCompleted() should not be called"); } /* * Serving OBEX GET operation is supported only in IdleState * * @see bcexchanger.comm.ExchangerState#onGet(javax.obex.Operation) */ public int onGet(Operation op) { return ResponseCodes.OBEX_HTTP_CONFLICT; } /* * Serving OBEX GET operation is supported only in * IdleState * * @see bcexchanger.comm.ExchangerState#onPut(javax.obex.Operation) */ public int onPut(Operation op) { // onPut is supported only in IdleState return ResponseCodes.OBEX_HTTP_CONFLICT; } }
package bcexchanger.comm; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import javax.microedition.io.Connector; import javax.obex.ClientSession; import javax.obex.HeaderSet; import javax.obex.Operation; import javax.obex.ResponseCodes; /** * * This class implements business card receiving state. In this state * an client OBEX GET operation is peformed. No OBEX server commands * are served. * * This class extends ExchangerState abstract class. * * @version 1.0 29.09.2005 * @see bcexchanger.comm.ExchangerState Design Patterns: State */ public class ReceiveState extends ExchangerState{ // Instance variables private ClientSession connection = null; private Operation operation = null; String url = null; boolean cancelInvoked = false; // true after cancelSending is // called /** * Constructor * * description * * @param _parent - * the class which is nesting the current state of the * state machine * @param _url - * a URL of the remote OBEX service. This state is responsible * for establishin the connection * @exception * @see */ public ReceiveState(ExchangerStateParent _parent, String url) { super(_parent); this.url = url; } /* * ReceiveState does not allow to start any other business card * exchange process. * * @see bcexchanger.comm.ExchangerState#startSending() */ public void startSending(int to) throws IOException { throw new IOException( "Receiving is in progress. Receiving has to be canceled before starting new sending process"); } /* * ReceiveState allows to cancel receiving process. * * @see bcexchanger.comm.ExchangerState#cancelSending() */ public void cancelSending() { cancelInvoked = true; try { // cancel any active operation running operation.abort(); } catch (IOException e) { // Ignore, aborting operation anyway } try { // send DISCONNECT command to the remote peer connection.disconnect(null); } catch (IOException e1) { // Ignore, disconnecting anyway } } /* * This method implements the logic for * receving a business card * */ public void doGet() { try { connection = (ClientSession) Connector.open(url); HeaderSet response = connection.connect(null); operation = connection.get(null); InputStream in = operation.openInputStream(); byte[] fullResult = null; { byte[] buf = new byte[256]; ByteArrayOutputStream bout = new ByteArrayOutputStream(2048); for (int len = in.read(buf); len >= 0; len = in.read(buf)) { bout.write(buf, 0, len); } fullResult = bout.toByteArray(); } ByteArrayInputStream bin = new ByteArrayInputStream(fullResult); DataInputStream din = new DataInputStream(bin); int size = din.readInt(); byte[] vCard = new byte[size]; din.read(vCard); parent.getListener().onReceiveBC(vCard); // End the transaction in.close(); int responseCode = operation.getResponseCode(); operation.close(); if (cancelInvoked) { throw new Exception( "Cancel did not invoke any exception; throw exception then"); } // receive is done try { parent.setState(new IdleState(parent)); parent.getListener().onReceiveComplete(ExchangerComm.DONE); } catch (Exception e) { } } catch (Exception e1) { if (cancelInvoked) { // if exception was caused by canceling parent.setState(new IdleState(parent)); parent.getListener() .onReceiveComplete(ExchangerComm.CANCELED); } else { // if exception was caused by error parent.setState(new IdleState(parent)); parent.getListener().onReceiveComplete(ExchangerComm.ERROR); } } finally { try { // finalizing operation operation.close(); } catch (IOException e3) { } try { // sending DISCONNECT command connection.disconnect(null); } catch (IOException e2) { // Ignore, disconnecting anyway } } } /* * Server OBEX GET command is supported only in * IdleState * * @see bcexchanger.comm.ExchangerState#onGet(javax.obex.Operation) */ public int onGet(Operation op) { return ResponseCodes.OBEX_HTTP_CONFLICT; } /* * Server OBEX PUT command is supported only in * IdleState * * @see bcexchanger.comm.ExchangerState#onPut(javax.obex.Operation) */ public int onPut(Operation op) { return ResponseCodes.OBEX_HTTP_CONFLICT; } }
package bcexchanger.comm; import java.io.IOException; import java.io.OutputStream; import javax.microedition.io.Connector; import javax.obex.ClientSession; import javax.obex.HeaderSet; import javax.obex.Operation; import javax.obex.ResponseCodes; /** * This class implements business card sending state. In this state a client * OBEX PUT operation is peformed. No OBEX server commands are served. * * This class extends ExchangerState abstract class. * * @version 1.0 29.09.2005 * @see bcexchanger.comm.ExchangerState Design Patterns: State */ public class SendState extends ExchangerState { // Instance variables private ClientSession connection = null; private String url = null; private Operation operation = null; private boolean cancelInvoked = false; /** * Constructor * * @param _parent - * the class which is nesting the current state of the state * machine * @param _url - * a URL of the remote OBEX service. This state is responsible * for establishin the connection */ public SendState(ExchangerStateParent _parent, String _url) { super(_parent); url = _url; } /* * SendState does not allow to start any other business card exchange * process. * * @see bcexchanger.comm.ExchangerState#startSending() */ public void startSending(int o) throws IOException { throw new IOException( "Sending is in progress. Sending has to be canceled before starting new sending process"); } /* * SendState allows to cancel sending process. * * @see bcexchanger.comm.ExchangerState#cancelSending() */ public void cancelSending() { cancelInvoked = true; try { // cancel any active OBEX operation operation.abort(); } catch (Exception e) { // catch NullPointer exception also // Ignore, aborting anyway } try { // send DISCONNECT to the remote peer connection.disconnect(null); } catch (Exception e1) { // catch NullPointer exception also // Ignore, disconnecting anyway } try { // close the connection connection.close(); // } catch (Exception e2) { // catch NullPointer exception also // Ignore, closing anyway } } /* * This method implements the logic for sending a * business card * */ public void doSend() { try { connection = (ClientSession) Connector.open(url); HeaderSet response = connection.connect(null); // Initiate the PUT request operation = connection.put(null); OutputStream out = operation.openOutputStream(); //getting the own card byte[] vCard = parent.getListener().getOwnBC(); int vlen = vCard.length; byte[] tmpBuf = new byte[vlen + 4]; System.arraycopy(vCard, 0, tmpBuf, 4, vlen); tmpBuf[0] = (byte) ((vlen >>> 24) & 0xff); tmpBuf[1] = (byte) ((vlen >>> 16) & 0xff); tmpBuf[2] = (byte) ((vlen >>> 8) & 0xff); tmpBuf[3] = (byte) ((vlen >>> 0) & 0xff); //sending data out.write(tmpBuf); out.close(); int responseCode = operation.getResponseCode(); operation.close(); if (cancelInvoked) { throw new Exception( "Cancel did not invoke any exception; throw exception then"); } // send is done try { parent.setState(new IdleState(parent)); } catch (Exception e) { } } catch (Exception e1) { parent.setState(new IdleState(parent)); if (cancelInvoked) { // if exception is caused by cancelation parent.getListener().onSendComplete(ExchangerComm.CANCELED); } else { // if exception is caused by error parent.setState(new IdleState(parent)); parent.getListener().onSendComplete(ExchangerComm.ERROR); } } finally { try { // finalizing operation operation.close(); } catch (IOException e3) { } try { // sending DISCONNECT command connection.disconnect(null); } catch (IOException e2) { // Ignore, disconnecting anyway } } } /* * Server OBEX GET command is supported only in IdleState * * @see bcexchanger.comm.ExchangerState#onGet(javax.obex.Operation) */ public int onGet(Operation op) { return ResponseCodes.OBEX_HTTP_CONFLICT; } /* * Server OBEX PUT command is supported only in IdleState * * @see bcexchanger.comm.ExchangerState#onPut(javax.obex.Operation) */ public int onPut(Operation op) { return ResponseCodes.OBEX_HTTP_CONFLICT; } }
package bcexchanger.comm; import java.io.IOException; import java.util.Enumeration; import java.util.Vector; import javax.bluetooth.DeviceClass; import javax.bluetooth.DiscoveryAgent; import javax.bluetooth.DiscoveryListener; import javax.bluetooth.LocalDevice; import javax.bluetooth.RemoteDevice; import javax.bluetooth.ServiceRecord; import javax.bluetooth.UUID; import javax.obex.Operation; import javax.obex.ResponseCodes; /** * * This class implements service discovery state. In this state an active * service discovery is peformed. No OBEX server commands are served. * * This class extends ExchangerState abstract class. This class also implments * DiscoveryListener interface to receive Bluetooth service discovery callbacks. * Service discoveries are run from the separate thread therefore the class * implements Runnable interface * * @version 1.0 29.09.2005 * @see bcexchanger.comm.ExchangerState javax.bluetooth.DiscoveryListener Design * Patterns: State * */ public class ServiceDiscoveryState extends ExchangerState implements DiscoveryListener, Runnable { // Instance variables private Thread serviceDiscoveryThread; private Vector services; private DiscoveryAgent agent; private int serviceDiscoveryID; private Vector devices; private boolean canceled = false; public static int GET = 0; public static int PUT = 1; /** * Constructor * * @param _parent - * the class which is nesting the current state of the state * machine * @param _devices - * a vecotor of RemoteDevice object representing devices found * during inquiry */ public ServiceDiscoveryState(ExchangerStateParent _parent, Vector _devices,int oper) throws IOException { super(_parent); canceled = false; services = new Vector(); devices = _devices; // initiate Bluetooth LocalDevice local = LocalDevice.getLocalDevice(); agent = local.getDiscoveryAgent(); serviceDiscoveryThread = new Thread(this); serviceDiscoveryThread.start(); } /* * ServiceDiscoveryState does not allow to start any other business card * exchange process. * * @see bcexchanger.comm.ExchangerState#startSending() */ public void startSending(int op) throws Exception { throw new IOException( "Service discovery is in progress. Service discovery has to be canceled before starting new sending process"); } /* * ServiceDiscoveryState allows to cancel discovery process. * * @see bcexchanger.comm.ExchangerState#cancelSending() */ public void cancelSending() { canceled = true; agent.cancelServiceSearch(serviceDiscoveryID); } /* * * Inquiry callbacks are not handled and not supposed to occur, since * inquiry process is not started * * @see javax.bluetooth.DiscoveryListener#deviceDiscovered(javax.bluetooth.RemoteDevice, * javax.bluetooth.DeviceClass) */ public void deviceDiscovered(RemoteDevice arg0, DeviceClass arg1) { throw new RuntimeException( "Internal error #8: ServiceDiscoveryState.deviceDiscovered() should not be called"); } /* * * Inquiry callbacks are not handled and not supposed to occur, since * inquiry process is not started * * @see javax.bluetooth.DiscoveryListener#inquiryCompleted(int) */ public void inquiryCompleted(int arg0) { throw new RuntimeException( "Internal error #9: ServiceDiscoveryState.inquiryCompleted() should not be called"); } public void servicesDiscovered(int id, ServiceRecord[] _services) { for (int i = 0; i < _services.length; i++) { services.addElement(_services[i]); } } public synchronized void serviceSearchCompleted(int arg0, int arg1) { notify(); } /* * (non-Javadoc) * * This methods implements logic of the service discovery process * * @see java.lang.Runnable#run() */ public synchronized void run() { Enumeration e = devices.elements(); // Business card exchanger service UUID UUID[] uuids = new UUID[1]; uuids[0] = new UUID(parent.getUUID(), false); // proceeded with all devices if not canceled while (e.hasMoreElements() && !canceled) { RemoteDevice device = (RemoteDevice) e.nextElement(); try { serviceDiscoveryID = agent.searchServices(null, uuids, device, this); } catch (Exception e0) { // signal error to the MIDlet parent.getListener().onServiceDiscoveryComplete( ExchangerComm.ERROR); parent.setState(new IdleState(parent)); return; } try { wait(); // wait until service search is done on this device } catch (Exception e1) { // Ignore } } if (canceled) { // handle user's cancelation try { parent.getListener().onServiceDiscoveryComplete( ExchangerComm.CANCELED); parent.setState(new IdleState(parent)); } catch (Exception e1) { throw new RuntimeException( "Internal error #10: ServiceDicoveryState.run()"); } } else { // not canceled if (services.isEmpty()) { // no services on devices try { parent.getListener().onServiceDiscoveryComplete( ExchangerComm.NO_RECORDS); parent.setState(new IdleState(parent)); } catch (Exception e1) { } } else if (services.size() == 1) { // one service found, // connect to it try { ServiceRecord serviceRecord = (ServiceRecord) services .firstElement(); parent.getListener().onServiceDiscoveryComplete( ExchangerComm.DONE); SendState state = new SendState(parent, serviceRecord .getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false)); parent.setState(state); state.doSend(); state = null; Thread.sleep(new Long(5000).longValue()); ReceiveState rstate = new ReceiveState(parent, serviceRecord .getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false)); parent.setState(rstate); rstate.doGet(); rstate = null; Thread.sleep(new Long(5000).longValue()); } catch (Exception e2) { } } else { // several services found, let user choose try { // list of friendly names of devices which contain services Vector friendlyNames = createFriendlyNamesList(services); int index = parent.getListener().resolveMultipleServices( friendlyNames); if (!canceled) { // if not canceled during resolving ServiceRecord serviceRecord = (ServiceRecord) services .elementAt(index); parent.getListener().onServiceDiscoveryComplete( ExchangerComm.DONE); SendState state = new SendState(parent, serviceRecord .getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false)); parent.setState(state); state.doSend(); state = null; Thread.sleep(new Long(5000).longValue()); ReceiveState rstate = new ReceiveState(parent, serviceRecord .getConnectionURL( ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false)); parent.setState(rstate); rstate.doGet(); rstate = null; Thread.sleep(new Long(5000).longValue()); } else { // if canceled during resolving parent.getListener().onServiceDiscoveryComplete( ExchangerComm.CANCELED); parent.setState(new IdleState(parent)); } } catch (Exception e1) { } } } } /* * This method creates a list of the friendly names of devices that contain * the servicies * * @param _services - vector of ServiceRecord objects representing found * services @return - vectors of strings containing friendly names of * devices */ private Vector createFriendlyNamesList(Vector _services) { Vector friendlyNames = new Vector(); Enumeration e = _services.elements(); while (e.hasMoreElements()) { ServiceRecord serviceRecord = (ServiceRecord) e.nextElement(); RemoteDevice device = serviceRecord.getHostDevice(); try { friendlyNames.addElement(device.getFriendlyName(false)); } catch (IOException e1) { // If there is an excption getting the friendly name // use the address friendlyNames.addElement(device.getBluetoothAddress()); } } return friendlyNames; } /* * Server OBEX GET command is supported only in IdleState * * @see bcexchanger.comm.ExchangerState#onGet(javax.obex.Operation) */ public int onGet(Operation op) { return ResponseCodes.OBEX_HTTP_CONFLICT; } /* * Server OBEX PUT command is supported only in IdleState * * @see bcexchanger.comm.ExchangerState#onPut(javax.obex.Operation) */ public int onPut(Operation op) { return ResponseCodes.OBEX_HTTP_CONFLICT; } }
package bcexchanger.ui; import java.util.Enumeration; import java.util.Vector; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Form; import javax.microedition.lcdui.Gauge; import javax.microedition.lcdui.Item; import javax.microedition.lcdui.List; import bcexchanger.*; /** * * This class represents a screen displaying the list of contacts of * the address book. The user can either choose the one contact or * exit the application * * This class peforms the reading of address book entries in the * separate thread, because reading the long address book might * take significant time * * The class is extending an example.BCExchanger.ui.Screen class and * implements Runnable interface. * * @see example.BCExchanger.ui.Screen Design patterns: State */ public class AddressBookScreen extends Screen implements Runnable { private Vector names; private Vector uids; private List list; private Command okCommand; private Command exitCommand; private Gauge gauge; private Form progressBar; private Thread thread; private Command progressExitCommand; /** * Constructor * * @param _midlet - * the parent class which keeps the current UI state */ public AddressBookScreen(BCExchangerMIDlet _midlet) { super(_midlet); // create and add commands to ui list progressBar = new Form("Please wait"); gauge = new Gauge("Reading contacts", false, Gauge.INDEFINITE, Gauge.CONTINUOUS_RUNNING); gauge.setLayout(Item.LAYOUT_CENTER); gauge.setLayout(Item.LAYOUT_VCENTER); progressBar.append(gauge); progressExitCommand = new Command("Exit", Command.EXIT, 1); progressBar.addCommand(progressExitCommand); progressBar.setCommandListener(this); displayable = progressBar; // start the thread reading the address book entries thread = new Thread(this); thread.start(); } public void commandAction(Command command, Displayable _displayable) { // in case this callback is called from wrong displayble if (_displayable != list && _displayable != progressBar) { throw new RuntimeException( "Internal error #14: AddressBookScreen.commandAction()"); } if (command == okCommand) { String uid = null; // determine the index try { uid = (String) uids.elementAt(list.getSelectedIndex()); } catch (Exception e) { throw new RuntimeException("Internal error #15"); } // save UID of the chosen card try { Storage.setBCUID(uid); midlet.changeScreen(new MainScreen(midlet)); } catch (Exception e1) { midlet.changeScreen(new AlertMessage(midlet, "Cannot save own business card", null)); } } else if (command == exitCommand || command == progressExitCommand) { midlet.quit(); } else { // unknown command throw new RuntimeException("Internal error #20"); } } /* (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { // get the list of names and UIDs Vector[] v; try { v = AddressBook.getNamesUIDLists(); names = v[0]; uids = v[1]; } catch (Exception e) { midlet.changeScreen(new AlertMessage(midlet, "Cannot read contact list data. Application will exit")); // quit // after // this // alert return; } // create a UI list of names list = new List("Choose own business card", List.EXCLUSIVE); Enumeration e = names.elements(); if (!e.hasMoreElements()) { // names list is empty midlet .changeScreen(new AlertMessage( midlet, "Contact list is empty. Please create own contact. Application will exit")); // quit // after // this // alert return; } else { while (e.hasMoreElements()) { // browse through names list and // add them to ui list list.append((String) e.nextElement(), null); } // create and add commands to ui list okCommand = new Command("Ok", Command.OK, 1); exitCommand = new Command("Exit", Command.EXIT, 1); list.addCommand(okCommand); list.addCommand(exitCommand); list.setCommandListener(this); displayable = list; midlet.changeScreen(this); } } }
package bcexchanger.ui; import javax.microedition.lcdui.Alert; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.Displayable; import bcexchanger.*; /** * * This class represents a screen displaying an alert message. The * class is extending an example.BCExchanger.ui.Screen class * * @version 1.0 20.09.2005 * @see example.BCExchanger.ui.Screen Design patterns: State */ public class AlertMessage extends Screen { Alert alert; Screen nextScreen = null; boolean quitAfterDismiss = false; /** * Constructor * * @param _midlet - * the parent class which keeps the current UI state * @param message - * text to display * @param _nextScreen - * UI screen should be displayed after the alarm message is * dismissed */ public AlertMessage(BCExchangerMIDlet _midlet, String message, Screen _nextScreen) { super(_midlet); nextScreen = _nextScreen; init(message); } /** * Constructor * * If this constructor is used, the application will exit after the * alert is dismissed * * @param _midlet - * the parent class which keeps the current UI state * @param message - * text to display */ public AlertMessage(BCExchangerMIDlet _midlet, String message) { super(_midlet); //quitAfterDismiss = true; init(message); } /** * Initialise the displayable * * @param message - * text to display */ private void init(String message) { alert = new Alert("", message, null, null); alert.setCommandListener(this); displayable = alert; } public void commandAction(Command command, Displayable _displayable) { if (_displayable != alert) { // wrong displayable throw new RuntimeException("Internal error #28"); } if (command == Alert.DISMISS_COMMAND) { if (quitAfterDismiss) { midlet.quit(); } else { if (nextScreen != null) { midlet.changeScreen(nextScreen); } } } else { // unknown command throw new RuntimeException("Internal error #29"); } } }
package bcexchanger.ui; import java.io.IOException; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.TextBox; import javax.microedition.lcdui.TextField; import bcexchanger.*; import bcexchanger.comm.ExchangerCommImpl; import bcexchanger.comm.ServiceDiscoveryState; /** * * This class implements the main screen of the application. Using * this screen user can initiate an exchange of business cards, change * own business card and exit the application * * The class is extending an example.BCExchanger.ui.Screen class * * @version 1.0 29.09.2005 * @see example.BCExchanger.ui.Screen Design patterns: State */ public class MainScreen extends Screen { private Command changeBCCommand; private Command exitCommand; private Command sendBCCommand; private TextBox textBox; private int typeOfOperation = ServiceDiscoveryState.PUT; /** * Constructor * * @param _midlet - * the parent class which keeps the current UI state */ public MainScreen(BCExchangerMIDlet _midlet) { super(_midlet); midlet.initComm(); // init happens only the first time MainScreen // is created String text = "Business Card Exchanger allows you to define your own business card " + "and exhange it with business cards of other users in vicinity " + "running the same application. The exchange is done over Bluetooth " + "and free of charge.\n\n" + "Use 'Change own card' to choose own card from the address book.\n" + "Use 'Exchange card' to initate the search and exchange of the cards.\n\n" + "The application is always waiting for connections from remote devices."; textBox = new TextBox("Business Card Exchanger", text, 999, TextField.UNEDITABLE); changeBCCommand = new Command("Change own card", Command.ITEM, 1); exitCommand = new Command("Exit", Command.EXIT, 1); sendBCCommand = new Command("Exchange cards", Command.ITEM, 1); textBox.addCommand(changeBCCommand); textBox.addCommand(exitCommand); textBox.addCommand(sendBCCommand); textBox.setCommandListener(this); displayable = textBox; } public void commandAction(Command command, Displayable _displayable) { if (_displayable != textBox) { throw new RuntimeException("Internal error #16"); } if (command == sendBCCommand) { try { setTypeOfOperation(ServiceDiscoveryState.PUT); ((ExchangerCommImpl)midlet.getExchanger()).startSending(ServiceDiscoveryState.PUT); String text = "Searching for devices..."; midlet.changeScreen(new ProgressScreen(midlet, text)); } catch (IOException e0) { midlet.changeScreen(new AlertMessage(midlet, "Cannot start inquiry "+e0, null)); } catch (Exception e1) { } } else if (command == changeBCCommand) { midlet.changeScreen(new AddressBookScreen(midlet)); } else if (command == exitCommand) { midlet.quit(); } else { // unknown command throw new RuntimeException("Internal error #18"); } } public int getTypeOfOperation() { return typeOfOperation; } public void setTypeOfOperation(int typeOfOperation) { this.typeOfOperation = typeOfOperation; } }
package bcexchanger.ui; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.Form; import javax.microedition.lcdui.Gauge; import javax.microedition.lcdui.Item; import bcexchanger.*; /** * * This class implements the screen which indicates that some * operation is in progress. For example when inquiry, service * discovery, business card sending and receiving is running this * screen is displayed. Using this screen user can cancel the processs * * The class is extending an example.BCExchanger.ui.Screen class * * @version 1.0 29.09.2005 * @see example.BCExchanger.ui.Screen Design patterns: State */ public class ProgressScreen extends Screen { private Command cancelCommand; private Gauge gauge; private Form progressBar; /** * Constructor * * @param _midlet - * the parent class which keeps the current UI state * @param message - * text message to be displayed */ public ProgressScreen(BCExchangerMIDlet _midlet, String message) { super(_midlet); progressBar = new Form("Please wait"); gauge = new Gauge(message, false, Gauge.INDEFINITE, Gauge.CONTINUOUS_RUNNING); gauge.setLayout(Item.LAYOUT_CENTER); gauge.setLayout(Item.LAYOUT_VCENTER); progressBar.append(gauge); cancelCommand = new Command("Cancel", Command.CANCEL, 1); progressBar.addCommand(cancelCommand); progressBar.setCommandListener(this); displayable = progressBar; } public void commandAction(Command command, Displayable _displayable) { if (_displayable != progressBar) { // wrong displayable throw new RuntimeException("Internal error #21"); } if (command == cancelCommand) { midlet.getExchanger().cancelSending(); } else { // unknown command throw new RuntimeException("Internal error #22"); } } }
package bcexchanger.ui; import javax.microedition.lcdui.CommandListener; import javax.microedition.lcdui.Display; import javax.microedition.lcdui.Displayable; import bcexchanger.*; /** * * This class is an abstract base class for all the UI screen in this * application. This class implements CommandListner interface. * * @version 1.0 29.09.2005 * @see example.BCExchanger.ui.Screen Design patterns: State * */ public abstract class Screen implements CommandListener { protected BCExchangerMIDlet midlet; protected Displayable displayable; /** * Constructor * * @param _midlet - * the parent class which keeps the current UI state */ public Screen(BCExchangerMIDlet _midlet) { midlet = _midlet; } /** * Makes current screen active * * Each class inherited from Screen has a displayable associated. * When this method is called the displayble is made visiable and * active * */ public void makeActive() { try { Display d = Display.getDisplay(midlet); if (d.getCurrent() != displayable) { // this prevents from // bringing application to // foreground in case is // application is in // background and the // current displayable is // the same as the one // made active d.setCurrent(displayable); } } catch (NullPointerException e) { throw new RuntimeException( "Internal error #2: Screen.midlet == null"); } } }
package bcexchanger.ui; import java.util.Enumeration; import java.util.Vector; import javax.microedition.lcdui.Choice; import javax.microedition.lcdui.Command; import javax.microedition.lcdui.Displayable; import javax.microedition.lcdui.List; import bcexchanger.*; /** * * This class implements the service list screen. This screen is used * to when several services are available in vicinity and allows user * to choose the desired one. Using this screen user can select the * service or cancel the processs * * The class is extending an example.BCExchanger.ui.Screen class * * @version 1.0 29.09.2005 * @see example.BCExchanger.ui.Screen Design patterns: State * */ public class ServiceListScreen extends Screen { private List list; private Command okCommand; private Command cancelCommand; /** * Constructor * * @param _midlet - * the parent class which keeps the current UI state * @param friendlyNames - * vector of the friednly names of the devices which * contain the services */ public ServiceListScreen(BCExchangerMIDlet _midlet, Vector friendlyNames) { super(_midlet); list = new List("Choose service", Choice.EXCLUSIVE); if (friendlyNames.isEmpty()) { // list should not be empty throw new RuntimeException("Internal error #23"); } Enumeration e = friendlyNames.elements(); while (e.hasMoreElements()) { list.append((String) e.nextElement(), null); } okCommand = new Command("Ok", Command.OK, 1); cancelCommand = new Command("Cancel", Command.CANCEL, 1); list.addCommand(okCommand); list.addCommand(cancelCommand); list.setCommandListener(this); displayable = list; } public void commandAction(Command command, Displayable _displayable) { if (_displayable != list) { // wrong displayable throw new RuntimeException("Internal error #24"); } if (command == okCommand) { midlet.serviceSelected(list.getSelectedIndex()); midlet.unblock(); } else if (command == cancelCommand) { midlet.getExchanger().cancelSending(); midlet.unblock(); } else { // unknown command throw new RuntimeException("Internal error #25"); } } }