|
|
Contents: |
|
|
|
Related content: |
|
|
|
Subscriptions: |
|
|
| Build Java apps, like an EPOC-based phone, for the Symbian
OS
Naveen
Balani (mailto:naveenbalani@rediffmail.com?cc=&subject=Writing
in EPOC) Technical Analyst, Syntel India, Ltd. 1 August 2002
EPOC-based devices running the Symbian OS, like Nokia's
9200-series phones, are popular worldwide, and they represent a
potentially powerful application platform. Here, Naveen Balani walks you
through the construction of a PersonalJava application that will run on
such a Symbian OS device. You'll see by example how your Java skills can
easily be turned to the construction of useful applications for phones
and handhelds.
This article will help you write Java applications on EPOC-based
devices. We will build a Java application that accesses a database of
contact information and deploy and test the application on a Nokia-based
emulator. To run the sample application, you'll need the Nokia 9200
Communicator Series SDK for Symbian OS (see the Resources
section below for this link and other useful information). This article
assumes that you are familiar with Java Swing programming, and gives you a
brief introduction to the JavaPhone 1.0 APIs, which give Java programs
access to a selection of native services on the phone used by our
application.
An overview of Java on Symbian
OS The basis of the Symbian OS Java environment is the
PersonalJava Application Environment, which implements version 1.1.1 of
Sun's specification; it is partly based on legacy code from Symbian's
Version 5 Java Runtime environment. Symbian OS also provides an
implementation of the JavaPhone 1.0 set of APIs, which gives Java programs
access to a selection of invaluable native services on the phone. These
include:
- Java Telephony API: Allows Java programs to create and
terminate outgoing calls, listen for and answer incoming calls, and
detect changes in the call state.
- Calendar and Address Book API: Allows Java programs to read,
write, and modify contact details, calendar items, and to-do items,
working transparently with data in native Symbian format. We will be
using the contact book APIs to store and retrieve our application
information.
javax.comm : Allows Java programs to discover,
configure, send, and receive data on available ports (serial and
infrared).
- P2P Wireless Datagram API: Allows Java programs to exchange
datagrams with other devices via UDP or SMS.
We can test the application we develop here on an emulator before
deploying it to an actual device. As noted, we will be using the Nokia
9200 Series Symbian OS SDK (Java edition), which can be downloaded from
the Forum Nokia Web site (see Resources).
The SDK includes an emulator, the PersonalJava application environment,
tools, and utilities to deploy and run the applications.
Building Java apps on the Symbian
platform Let's take a look at the high-level steps required
to build a Java application on the Symbian platform:
- Write the Java code in your favorite editor.
- Compile the code to Java class files.
- Create the
<APPNAME>.txt and
<APPNAME>.aif support files. (These are needed if you
want the application to appear on the phone's extras bar and to have its
own icon on the phone's screen.)
- Test under the WINS emulator (this requires a PC running Windows
95/98/NT/2000).
- Copy the class,
.txt , and .aif files to
the target machine. The class files do not have to be recompiled.
- Test your application on the device.
- Create an SIS installation file for easy deployment of your
application.
We'll be following the first four steps in the list to develop our
sample application. For more on deploying the completed app to a real
Symbian OS device, see Resources.
Introduction to the Java Phone
APIs As our application relies on the Java Phone APIs for
storing data in Symbian format, we'll look at some of them, namely
javax.pim.addressbook and javax.pim.database ,
before going into the details of our code. Using these APIs, we can store
and retrieve our contact information.
The ContactDatabase class is an abstraction modeling a
database storing contact information. ContactDatabase
implements the javax.pim.database.Database interface and
stores information associated with contacts. This information is organized
in the form of Item s. A ContactCard , which
stores information about an individual contact, is a subclass of
Item that can be retrieved from the
ContactDatabase .
To open the contact database, we can use the static method
ContactDatabase.openDatabase() . After we've opened the
database, we can manipulate it. Each row of a database corresponds to a
ContactCard object. This object stores details of a contact
in ItemField s, which can be aggregated further to produce
AggregateFields .
The ContactDatabase class has static final fields that
correspond to the name and value fields (and parameter name and parameter
value fields) of the vCard specification (see Resources).
If a field in the vCard specification has property named N ,
then the ContactDatabase will contain a static
String field called N . The
ItemField s of ContactCard map to these fields.
Hence, the ContactCard equivalent field to the vCar property
named N will be a static String field
ContactDatabase.N .
For example, a few of the fields in the ContactDatabase
class are listed below:
public static final java.lang.String N
public static final java.lang.String FN
public static final java.lang.String TEL
public static final java.lang.String ADR
These specify name, formatted name, telephone number, and structured
delivery address, respectively.
A field such as ContactDatabase.N can be an aggregate
composed of up to five parts, as follows, where the integer represents the
index of the respective component part.
FAMILY_NAME
GIVEN_NAME
ADDITIONAL_NAMES
PREFIXES
SUFFIXES
These part values are defined as fields of
ContactDatabase . For instance: Listing 1.
Defining the fields of ContactDatabase
public static final int FAMILY_NAME
|
is the index in the N field of the family name (and has
the value 0). Similarly: Listing 2. Defining more
fields
public static final int GIVEN_NAME
|
is the index in the N field of the given name (and has the
value 1).
An ItemField can have a parameter value associated with
it. A parameter has a name and a value. In a ContactDatabase
the name is always TYPE : Listing 3.
Naming a parameter
public static java.lang.String TYPE
|
Some example values are given below in Listing 4. Listing 4. Further example values
public static final java.lang.String HOME
public static final java.lang.String WORK
|
The values are all TEL field TYPE parameter
values referring to home and work numbers, respectively. Similarly,
Listing 5 includes some of the parameter values associated with an
ADR field: Listing 5. Parameter values
associated with ADR
public static final java.lang.String HOME
public static final java.lang.String WORK
|
These indicate home and work addresses, respectively. We will be using
the above fields while building our Java phone book application.
A record is inserted into the contact database in the form of contact
cards. The contact cards contain ItemField s or an
AggregateField . This can be added by using the
contactCard addItem() method: Listing 6.
Inserting a new record
ContactCard contactCard = new ContactCard();
ItemField name = new ItemField(ContactDatabase.N, "Value");
contactCard.addItem(name);
|
To retrieve the data, we could use the items() method of
the ContactDatabase to search for a particular record: Listing 7. Searching for a record
private ContactDatabase addressbook;
Iterator result = addressbook.items(ContactDatabase.N, "Value To Search");
|
After getting the result, we could get each record as follows (we'll
use ContactCard as our example): Listing
8. Getting each record
ContactCard contact = (ContactCard)(results.next());
|
After getting the ContactCard s, we could use the
getFields() or getField() method to get
ItemField or AggregrateField : Listing 9. Retrieving data
AggregateField name = (AggregateField)contact.getField("N");
if(name != null)
{
name.getField(ContactDatabase.FAMILY_NAME).getString(); // Index 0
name.getField(ContactDatabase.GIVEN_NAME).getString(); // Index 1
}
|
With this information under our belts, we now have sufficient knowledge
to build and run our sample application.
Sample application: code
analysis Download the sample.zip file from Resources
below. This file contains the javaphonebook directory;
extract it to your C: directory. The
javaphonebook directory contains the
JavaPhoneBook.java application. This application represents a
phone book, which stores contact information using the Java Phone APIs we
discussed in the previous section.
Let's analyze the code in JavaPhoneBook.java in some
detail, starting with line 40. Listing 10. Setting up
a keyboard listener
public class JavaPhoneBook extends CFrame implements CBAListener
|
Because Nokia 9200 Series communicators do not, by default, have a
pointer, the user interacts with applications via the keyboard, also known
as the Command Button Array (CBA). Hence the
JavaPhoneBook extends CFrame with
implements CBAListener so that user interaction takes place
through CBActionEvents . The CBA responds to
CBActionEvents and hence the CFrame (which
extends the Frame class) implements a
CBAListener interface, which in turn extends the
EventListener interface.
Lines 43 to 47 define the TextFields for user inputs
firstname , lastname , workph ,
address , and homeph . Listing 11. Defining TextFields for user input
private TextField firstName = new TextField(12) // and so on;
|
Lines 49 to 52 define the Menu and MenuItem
for the add, find, and clear actions, which can be accessed from drop-down
menus when the application is deployed and run. (You'll see how this works
in the next section.) Listing 12. Defining drop-down
menu
49 private Menu editMenu=new Menu("Main Menu");
private MenuItem createItem=new MenuItem("Add Information",new MenuShortcut(KeyEvent.VK_N));
private MenuItem findItem=new MenuItem("Find Information", new MenuShortcut(KeyEvent.VK_F));
private MenuItem clearItem=new MenuItem("Clear Fields", new MenuShortcut(KeyEvent.VK_C));
|
Line 54 creates an instance of the CBAHandler ; lines 56 to
58 create a new CBA for add, find, and close operations. Listing 13. Creating a new CBA
static final int ADD = EikCommandButtonGroup.BUTTON1;
static final int FIND = EikCommandButtonGroup.BUTTON2;
static final int CLOSE = EikCommandButtonGroup.BUTTON4;
|
In lines 60 to 62, we declare variables for accessing the
ContactDatabase , ContactCard , and
Iterator to iterate over ContactCards . Listing 14. Declaring variables
private ContactDatabase addressbook; // The contact database
private Iterator results; // Used to iterate through the search results.
private ContactCard currentContact; // The currently active ContactCard
|
Lines 71 to 90 build the UI part of the application. Lines 98 to 107
create ActionListener s for menu events and handlers to catch
CBA events. Listing 15. Building a UI
editMenu.add(createItem);
editMenu.add(findItem);
editMenu.add(clearItem);
menuBar.add(editMenu);
createItem.addActionListener(this);
findItem.addActionListener(this);
clearItem.addActionListener(this);
// Add the CBA Handler Events
cba = new CBAHandler(this);
cba.setText(ADD, "Add \n Information");
cba.setText(FIND, "Find \n Information");
cba.setText(CLOSE, "Close");
cba.activate();
|
Line 113 calls the openDataBase() function that opens the
contact address database. Listing 16. Opening the
contact address database
addressbook = ContactDatabase.openDatabase()
|
Lines 126 to 146 react to user actions, calling the add, find, and
close functions, as appropriate. If the action is add, for instance, the
createRecord() function is executed. This function creates an
AggregateField (as discussed earlier) to store user-entered
names and addresses, and an ItemField to store work and home
phone numbers in the form of name-value pairs. Listing 17. Calling user-requested functions
//Name in this order: Family name, given name
AggregateField name = new AggregateField(ContactDatabase.N);
name.addField(new ItemField(ContactDatabase.N, lastName.getText())); // Family name
name.addField(new ItemField(ContactDatabase.N, firstName.getText())); // Given name
//Work telephone number
ItemField workTel = new ItemField(ContactDatabase.TEL, workph.getText());
workTel.addParameter(new Parameter(ContactDatabase.TYPE, ContactDatabase.WORK));
//Home telephone number
ItemField homeTel = new ItemField(ContactDatabase.TEL, homeph.getText());
homeTel.addParameter(new Parameter(ContactDatabase.TYPE, ContactDatabase.HOME));
//Organization : for storing address
AggregateField org = new AggregateField(ContactDatabase.ORG);
org.addField(new ItemField(ContactDatabase.ORG, address.getText()));
|
Next, lines 215 to 233 create a ContactCard , as shown in
Listing 17 below, to store the fields we created above. We then add that
ContactCard to the contact database. Thus, our
ContactCard holds the AggregrateField and
ItemField s. Listing 18. Creating a
ContactCard
ContactCard contact = new ContactCard();
contact.addField(name);
contact.addField(workTel);
contact.addField(homeTel);
contact.addField(org);
// Add the ContactCard record and ensure we can also update or delete it
currentContact = (ContactCard)addressbook.addItem(contact);
|
In this way, a record (in the form of a ContactCard ) is
inserted into the ContactDatabase .
When we do a find operation, we search based on the
lastName field. Thus, when the user clicks on the Find
Information button, the displayRecord() routine is executed.
Basically, we use the items() method of the
ContactDatabase as discussed earlier to get the
ContactCard with a lastName field that matches
the text entered by the user. The logic starts on line 175: Listing 19. Beginning of find operation logic
Iterator results = addressbook.items(ContactDatabase.N, lastName.getText());
|
Lines 247 to 289 get the ContactCard and retrieve the
values associated with it. First we use the getField("N")
method of the ContactCard APIs to retrieve the
AggregateField consisting of names; then we retrieve the
values of the first and last name from the AggregateField
object. We follow the same procedure for the remaining fields and display
their values in the appropriate fields in our user interface. Listing 20. Find operation, continued
ContactCard contact = (ContactCard)(results.next());
currentContact = contact;
// Get the name Aggregate field and split it apart to retrieve last and first name
AggregateField name = (AggregateField)contact.getField("N");
if(name != null)
{
lastName.setText(name.getField(ContactDatabase.FAMILY_NAME).getString());
firstName.setText(name.getField(ContactDatabase.GIVEN_NAME).getString());
}
|
Now that we've taken a look at the high points of our application's
code, let's see how to build it so you can experiment with it on your
own.
Building the
application Run the buildPhone.bat file that
compiles the JavaPhoneBook.java file and creates
JavaPhoneBook.jar in that same directory.
buildPhone.bat assumes that the %epocroot%
variable has been set; this generally happens during installation of the
Symbian Nokia SDK. %epocroot% points to
C:\Symbian\6.0\NokiaJava\erj , where C:\Symbian\
is the directory where you have installed the SDK.
Next, we shall create the classes that need to be deployed to the
Symbian Environment on the Nokia emulator. Version 6.0 of the Symbian
platform is delivered as two complete communicator reference designs (aka
DRFDs): Quartz, for pocket-sized, palmtop devices, and
Crystal, for powerful, keyboard-based, wireless information
devices. Each device family has a shell program -- Extras on Crystal, and
the Launcher on Quartz -- that allows the user to run application
programs. Quartz is a tablet communicator with convenient pocket-sized,
palmtop form factor.
To run an application from either shell program, you need at least the
following two files:
- An EPOC application file (
.app ), which, at minimum,
contains a unique identifier (UID) to identify the program.
- An application information file (
.aif ) to specify the
icon and caption that will represent the phone on a menu screen.
Other files might also be required, depending on the programming
language of the application. For example, a Java program needs an
additional text file (.txt ) to specify the correct command
line to the JVM when the application is launched.
AIF Builder is an application that runs on your development machine
(not the device or the emulator) to help you easily create the required
files. It works with several device families and programming
languages.
Open the AIF Tool Builder from Program Files -> Symbian 6.0SDK ->
Development Tools. Select New from the File menu, and select Java as the
application language in the Application Details pane, as shown in Figure
1.
Figure 1. AIF Builder Application Details
pane
Type in JavaPhoneBook in the Application Name field. For
Application UID, type in 0x01000000 , which uniquely
identifies the application. Each application must have its own UID. A UID
is a 32-bit number, which you can get as you need from Symbian (see Resources).
During development, you can choose temporary UIDs within the range from
0x01000000 to 0x0fffffff . Care must still be
taken to ensure that no two programs have the same UID; such a conflict
could stop a program from loading correctly, typically leading to
NotFound errors.
Finally, type in the following as the Java Command Line Text field
value:
-cp JavaPhoneBook.jar -Dcom.symbian.appName="JavaPhoneBook" navexamples.JavaPhoneBook
|
This tells the JVM on the emulator or device to run the appropriate
class file for the JavaPhoneBook application.
Next, select the DRFD's pane (see Figure 2) to select the device family
or families for which the application is written. In the DRFD's To
Generate For option list, check only the box next to Crystal, as we are
developing for Crystal-based devices. In the Location to Generate Files
field, select the directory where you have the
JavaPhoneBook.jar file and the files extracted from
sample.zip ; you can use any existing folder as the output
directory for temporary files.
Figure 2. AIF Builder DRFDs pane
Click on the Generate button and four files will be created:
JavaPhoneBok.aif , JavaPhoneBok.app ,
JavaPhoneBok.txt , and a temporary file named
JavaPhoneBook.rss .
Running the
application We'll need to move the
JavaPhoneBook folder to the applications directory where the
emulator or device can find it. Copy the directory to
C:\Symbian\6.0\NokiaJava\Epoc32\Wins\c\System\Apps . Note that
you only need the three files we generated at the end of the last section
(excluding the temporary file) plus the JavaPhoneBook.jar , so
the remaining files in the JavaPhoneBook directory can be
deleted. When you run the emulator, the application JavaPhoneBook will now
appear in the Extras section of the emulator menu as shown in Figure 3
below. (To run the emulator, select Program Files- > Symbian 6.0 SDKS
-> Nokia Java -> Emulator(rel).)
Figure 3. Emulator Extras menu
Click on Open and the screen in Figure 4 appears. Enter the information
requested and select Add Information, which is nothing but a CBA button
that fires the appropriate createRecord() method. The
information is added to the ContactDatabase .
Figure 4. Adding an address to the
database
You could also add information from the menu section. Press the F1 key
on your keyboard to access the drop-down menus. Select Main Menu -> Add
Information as shown in Figure 5. This menu event corresponds to the menu
events that we have programmed.
Figure 5. Using a drop-down menu
Select Clear Fields from the Main Menu to clear all the information.
Then type in the last name of the person for whom you want to search and
click Find Information as shown in Figure 6.
Figure 6. Searching the database
You should see the data being populated in the remaining fields if the
last name is found in the ContactDatabase , as shown in Figure
7.
Figure 7. Search results
Thus, we have successfully built our phone book application.
Deploying the application on an actual
device To deploy our application to a real Symbian OS
device, we must create a Symbian installation (SIS) for it. The SDK
includes the necessary tool for this, an executable named
makesis.exe . This tool needs a .pkg (package)
file as input that contains the application information and files that
need to be deployed. Refer to the Symbian developers' Web site (see Resources)
to learn how to create an SIS file for any application.
Conclusion We've now built
a sample Java-based application on the Symbian platform and deployed and
tested it on our Nokia emulator. This application build can be transferred
on to our actual Nokia 9200 series devices. You should now be able to use
your existing Java background and your new knowledge of the Java Phone
APIs to start developing applications for this useful class of devices.
Now you need to put your creativity to work and think about what kind of
apps Symbian users need!
Resources
- To work with the examples on your desktop machine, you need to
download the Nokia 9200 Communicator Series SDK for Symbian OS from Nokia's Web site.
- Download
sample.zip,
which contains the full code of this article's sample
application.
- Download
download.zip,
which contains a batch file, cascading style sheet, and MS DOS
code.
- Sun's PersonalJava
FAQ explains how PersonalJava fits in with the J2ME
platform.
- For more on J2ME and how PersonalJava will fit into its structure,
see "J2ME
grows up," by Todd Sundsted (developerWorks, May
2001).
- Learn more about the vCard protocol.
- Once you've completed your Symbian-based application, you'll need to
go to the Symbian Web site
to get a unique UID for it. This site also has more information on
creating SIS files with which you can deploy your application to a real
device.
- Check out the latest initiatives at IBM's pervasive
computing site.
- Build Java apps with VisualAge
for Java.
About the
author Naveen Balani works as a technical analyst for
Syntel India, Ltd. His job profile includes designing, developing,
and implementing J2EE-based products. You can reach Naveen at naveenbalani@rediffmail.com.
|
|