Contact Sample Application

In order to become familiar with the PDAP PIM API, you will now build a simple PIM sample application. Although the sample will focus on the address book part of the API, it will be designed in a structured way that allows you to reuse many parts as building blocks for your own, more powerful PIM applications.

For the PIM sample application, you will first design a simple dialog that allows you to edit a single contact, including fields with subtypes. The second building block of the application is the main window, showing the list of contacts stored in the default ContactList of the device.

An Edit Dialog for Contacts

For the regular contact fields, it is obvious how to present them to the user in a graphical user interface. You just put them in two grid layouts contained in the right and center areas of a border layout; the left grid shows the labels and the center one the input fields, as in many other examples. However, for the fields supporting multityped values such as TEL or FAX, the task becomes a bit more complex.

One option to make all subtypes accessible in a convenient way is to create separate panels for each subtype such as WORK, HOME, and so on using a CardLayout. A Choice element can then be used to switch between the panels.

This approach is shown in Listing 7.1. Figure 7.2 shows a screenshot of the contact dialog. The panels are represented by the inner class FieldPane. FieldPane contains an integer variable type representing the subtype of all contained fields. The method addField() adds a field with the given label and ID. In order to be able to synchronize the user interface with a concrete Contact instance, the FieldInfo helper class is used to store the link between the TextField in the user interface and the ID and type of the corresponding field. All FieldInfo objects are stored in the vector fieldList. When the dialog is filled from a contact in the edit method, all field info objects are iterated, and the TextFields are filled according to the corresponding content of the contact. If the user confirms the dialog, the FieldInfo objects are iterated again, and the contents of the TextFields, possibly altered by the user, are transferred back to the contact. Note that the transfer to a contact object always needs to be committed to the database in order to become persistent.

Figure 7.2. The ContactDialog showing a sample contact.


The addField() method of the FieldPane inner class is not accessed in the ContactDialog directly, but ContactDialog has its own addField() method. Depending on the given type parameter, the STRING fields are added directly to the main FieldPane, whereas fields with MULTIPLE subtypes are distributed to all corresponding panels. For this purpose, the subtypes supported by the platform are queried using the getSupportedTypes() method. Then, for each type, addSub() is called. In addSub(), the addField() method of the corresponding FieldPane is called. Additional panels and choice entries are created as needed.

Note that the given add method supports string and multitype fields only, but no date or binary fields. However, it should not be a problem to support those types by adding corresponding constants and user interface elements.

Listing 7.1. ContactDialog.java—A Dialog for a Single Contact
import java.awt.*;
import java.awt.event.*;
import javax.microedition.pim.*;
import java.util.Hashtable;
import java.util.Vector;

public class ContactDialog extends Dialog
    implements ItemListener, ActionListener {

    Hashtable cards = new Hashtable();
    Contact contact;
    ContactList contactList;
    Panel cardPane = new Panel(new CardLayout());
    Choice typeChoice = new Choice();
    Button okButton = new Button("ok");
    Button cancelButton = new Button("cancel");
    boolean result = false;
    Vector fieldList = new Vector();

    class FieldInfo {
        int id;
        int type;
        TextField field;
    }

    class FieldPane extends Panel {
        Panel labels = new Panel(new GridLayout(0, 1));
        Panel fields = new Panel(new GridLayout(0, 1));
        int type;

        FieldPane(int type) {
            super(new BorderLayout());
            this.type = type;
            add(labels, BorderLayout.WEST);
            add(fields, BorderLayout.CENTER);
        }

        void addField(int id, int type) {
            FieldInfo info = new FieldInfo();
            info.id = id;
            info.field = new TextField(30);
            info.type = type;
            labels.add(new Label(contactList.getFieldLabel(id)));
            fields.add(info.field);
            fieldList.addElement(info);
        }
    }

    public void actionPerformed(ActionEvent ev) {
        result = ev.getSource() == okButton;
        hide();
    }

    void addSub(int id, String type, int typeId) {
        FieldPane fieldPane = (FieldPane) cards.get(type);
        if (fieldPane == null) {
            fieldPane = new FieldPane(typeId);
            typeChoice.add(type);
            cards.put(type, fieldPane);
            Panel compact = new Panel(new BorderLayout());
            compact.add(fieldPane, BorderLayout.NORTH);
            ScrollPane sp = new ScrollPane();
            sp.add(compact);
            cardPane.add(sp, type);
        }
        fieldPane.addField(id, typeId);
    }

    void addField(int id) {
        if (id == Contact.UID || id == Contact.REVISION)
            return;
        int dataType = contact.getDataType(id);
        if (dataType == PIMElement.STRING)
            addSub(id, "Main", -1);
        else if (dataType == PIMElement.STRING_TYPED) {
            int[] types = contactList.getSupportedTypes(id);
            for (int i = 0; i < types.length; i++) {
                switch (types[i]) {
                    case Contact.TYPE_HOME :
                        addSub(id, "Home", types[i]);
                        break;
                    case Contact.TYPE_WORK :
                        addSub(id, "Work", types[i]);
                        break;
                                       // add other types here
                }
            }
        }
        // other data types are ignored
    }

    public void itemStateChanged(ItemEvent e) {
        CardLayout cl = (CardLayout) (cardPane.getLayout());
        cl.show(cardPane, (String) e.getItem());
    }

    public ContactDialog(Frame frame, ContactList contactList) {
        super(frame, "Edit Contact", true);
        this.contactList = contactList;
        contact = contactList.createContact();
        add(typeChoice, BorderLayout.NORTH);
        add(cardPane, BorderLayout.CENTER);
        typeChoice.addItemListener(this);
        int[] ids = contactList.getSupportedFields();
        for (int i = 0; i < ids.length; i++)
            addField(ids[i]);
        Panel buttonPane = new Panel();
        buttonPane.add(okButton);
        buttonPane.add(cancelButton);
        okButton.addActionListener(this);
        cancelButton.addActionListener(this);
        add(buttonPane, BorderLayout.SOUTH);
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent ev) {
                result = false;
                hide();
            }
        } );
        pack();
    }

    public boolean edit(Contact contact, String title) {
        setTitle(title);
        this.contact = contact;
        result = false;
        for (int i = 0; i < fieldList.size(); i++) {
            FieldInfo info = (FieldInfo) fieldList.elementAt(i);
            String text =
                contact.getDataType(info.id) == PIMElement.STRING
                    ? contact.getString(info.id)
                    : contact.getTypedString(info.id, info.type);
            info.field.setText(text == null ? "" : text);
        }
        show();
        if (result) {
            for (int i = 0; i < fieldList.size(); i++) {
                FieldInfo info = (FieldInfo) fieldList.elementAt(i);
                String text = info.field.getText();
                if (text.equals(""))
                    text = null;
                if (contact.getDataType(info.id) == PIMElement.STRING)
                    contact.setString(info.id, text);
                else
                    contact.setTypedString(info.id, info.type, text);
                info.field.setText(text == null ? "" : text);
            }
            contact.commit();
        }
        return result;
    }
}

The PimDemo MIDlet

Having implemented the dialog for editing a single contact, the main application is relatively simple. You just need to display a list of all entries contained in the address book, obtained from the corresponding ContactList. Listing 7.2 shows a corresponding sample application. Figure 7.3 shows the PimDemo MIDlet.

Figure 7.3. The PIMDemo application showing a list of all contacts added to the contact database.


Because the displayed names of the entries might not be unique, it makes sense to store the UIDs in a separate vector. Of course, it would be possible to keep all the Contact objects in the vector directly, but this would demand a lot of heap memory, which might not be available on the PDA. The stored UIDs are used in the getContact() method, where a given UID is translated back to a Contact object using the filtered contact enumeration.

The application displays buttons to add, edit, or remove an entry. When the add or edit button is pressed, a ContactDialog is shown, and the user can edit the content of the fields displayed. When the program control returns from the dialog, and the dialog was not cancelled, the changes are made persistent by calling the addElement() or updateElement() method of the ContactList.

Listing 7.2. FieldDialog.java—A Dialog, Based on the FieldDescription Class, for Editing Single Item Fields
import java.awt.*;
import java.awt.event.*;
import javax.microedition.midlet.*;
import javax.microedition.pim.*;
import java.util.*;

public class PimDemo extends MIDlet implements ActionListener {
    ContactList contactList = PIM.openContactList(PIM.READ_WRITE);
    Frame frame = new Frame("PimDemo");
    ContactDialog contactDialog = new ContactDialog(frame, contactList);
    java.awt.List list = new java.awt.List();
    Vector uids = new Vector();
    Button editButton = new Button("edit");
    Button addButton = new Button("add");
    Button deleteButton = new Button("delete");
    Button exitButton = new Button("exit");

    public PimDemo() {
        for (Enumeration e = contactList.elements(); e.hasMoreElements();) {
            Contact c = (Contact) e.nextElement();
            uids.addElement(c.getString(Contact.UID));
            list.add(getLabel(c));
        }
        Panel buttonPane = new Panel();
        buttonPane.add(editButton);
        buttonPane.add(addButton);
        buttonPane.add(deleteButton);
        buttonPane.add(exitButton);
        editButton.addActionListener(this);
        addButton.addActionListener(this);
        deleteButton.addActionListener(this);
        exitButton.addActionListener(this);
        frame.add(buttonPane, BorderLayout.SOUTH);
        frame.add(list, BorderLayout.CENTER);
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent ev) {
                destroyApp(true);
                notifyDestroyed();
            }
        } );
        frame.pack();
    }

    Contact getContact(String uid) {
        Contact template = contactList.createContact();
        template.setString(Contact.UID, uid);
        Enumeration e = contactList.elements(template);
        return (Contact) e.nextElement();
    }

    String getLabel(Contact c) {
        String label = c.getString(Contact.FORMATTED_NAME);
        if (label == null)
            label =
                "" + c.getString(Contact.NAME_FAMILY) + ", " +
                 getString(Contact.NAME_GIVEN);
        return label;
    }

    public void actionPerformed(ActionEvent ev) {
        System.out.println("ev: " + ev);
        if (ev.getSource() == addButton) {
            Contact c = contactList.createContact();
            if (contactDialog.edit(c, "New Contact")) {
                list.add(getLabel(c));
                uids.addElement(c.getString(Contact.UID));
            }
        }
        else if (ev.getSource() == exitButton) {
            destroyApp(true); // clean up
            notifyDestroyed();
        }
        else if (ev.getSource() == editButton || ev.getSource() ==
          deleteButton) {
            int index = list.getSelectedIndex();
            if (index == -1)
                return;
            Contact c = getContact((String) uids.elementAt(index));
            if (ev.getSource() == deleteButton) {
                contactList.deleteElement(c);
                list.remove(index);
                uids.removeElementAt(index);
            }
            else if (contactDialog.edit(c, getLabel(c))) {
                list.replaceItem(getLabel(c), index);
            }
        }
    }

    public void pauseApp() {
    }

    public void startApp() {
        frame.show();
    }

    public void destroyApp(boolean uncond) {
        contactList.close();
    }
}

What Is Missing?

The PimDemo sample application shows basic access to the PIM API, but for a real PIM application, a lot of functionality is missing. For example, categories are not supported, and the dialog shows only a fraction of the fields available. Event and to-do entries are not supported at all. Field types other than string and multityped fields are not supported. The contact list is not sorted, and a search function is missing. The delete button lacks a confirm dialog.

Use the example as a starting point for your own ideas and extensions, and feel free to reuse the code in your own applications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset