You are on page 1of 188

BlackBerry Java Development

Environment
Version 3.6

Developer Guide

Volume 2 – Advanced Topics


BlackBerry Java Development Environment Version 3.6
Developer Guide Volume 2 – Advanced Topics
Last modified: 24 March 2003

Part number: PDF-05772-001


At the time of publication, this documentation complies with the BlackBerry Java Development
Environment and BlackBerry Wireless Handheld version 3.6.
The information in this document is RIM confidential and is for internal distribution only.

© 2003 Research In Motion Limited. All Rights Reserved. The BlackBerry and RIM families of
related marks, images and symbols are the exclusive properties of Research In Motion Limited.
RIM, Research In Motion, ‘Always On, Always Connected’, the “envelope in motion” symbol and
the BlackBerry logo are registered with the U.S. Patent and Trademark Office and may be pending
or registered in other countries. All other brands, product names, company names, trademarks and
service marks are the properties of their respective owners.

The handheld and/or associated software are protected by copyright, international treaties and
various patents, including one or more of the following U.S. patents: 6,278,442; 6,271,605; 6,219,694;
6,075,470; 6,073,318; D445,428; D433,460; D416,256. Other patents are registered or pending in
various countries around the world. Visit www.rim.net/patents.shtml for a current listing of
applicable patents.

While every effort has been made to achieve technical accuracy, information in this document is
subject to change without notice and does not represent a commitment on the part of Research In
Motion Limited, or any of its subsidiaries, affiliates, agents, licensors, or resellers. There are no
warranties, express or implied, with respect to the content of this document.

Research In Motion Limited


295 Phillip Street
Waterloo, ON N2L 3W8
Canada

Research In Motion Europe


Centrum House, 36 Station Road
Egham, Surrey TW20 9LF
United Kingdom
Published in Canada
This product includes software developed by the Apache Software Foundation
(http://www.apache.org/). Such software is subject to the terms and conditions below:

Copyright (c) 1999-2000 The Apache Software Foundation. All rights reserved. Redistribution and use in source and binary
forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: “This
product includes software developed by the Apache Software Foundation (http://www.apache.org/).” Alternately, this
acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear.

4. The names “Xerces” and “Apache Software Foundation” must not be used to endorse or promote products derived from this
software without prior written permission. For written permission, please contact apache@apache.org.

5. Products derived from this software may not be called “Apache”, nor may “Apache” appear in their name, without prior
written permission of the Apache Software Foundation.

THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
========================================================================================
This software consists of voluntary contributions made by many individuals on behalf of the Apache Software Foundation and
was originally based on software copyright (c) 1999, International Business Machines, Inc., http://www.ibm.com. For more
information on the Apache Software Foundation, please see <http://www.apache.org/>.
Contents
About this guide.............................................................................................. 9
Overview ...........................................................................................................10
Audience ............................................................................................................10
Conventions ......................................................................................................11
Related resources..............................................................................................11

CHAPTER 1 Using controlled APIs.................................................................................... 13


Controlled APIs ................................................................................................14
About code signatures .....................................................................................15
Registering for code signing ...........................................................................16
Requesting code signatures ............................................................................19

CHAPTER 2 Integrating email........................................................................................... 23


BlackBerry Mail API.........................................................................................24
Working with messages ..................................................................................26
Working with folders.......................................................................................32
Working with attachments..............................................................................35
Testing an application in the simulator.........................................................38
Deploying an application ................................................................................40

CHAPTER 3 Integrating PIM functions............................................................................. 41


PIM API overview ............................................................................................42
Address book ....................................................................................................45
Tasks ...................................................................................................................54
Calendar.............................................................................................................61
Deploying your application ............................................................................69

CHAPTER 4 Adding options.............................................................................................. 71


Options API.......................................................................................................72
Registering to add options ..............................................................................72
Adding an option item ....................................................................................73
Storing options..................................................................................................75
Providing access to option data......................................................................76
CHAPTER 5 Storing persistent data ................................................................................. 79
Choosing how to store data ............................................................................80
Working with persistent data .........................................................................83
Working with custom objects .........................................................................88

CHAPTER 6 Backing up and restoring persistent data................................................... 97


Synchronization API ........................................................................................98
Adding support for backing up data...........................................................100
Creating an initialization project..................................................................106
Testing an application in the simulator.......................................................114

CHAPTER 7 Notifications ................................................................................................115


Notification API..............................................................................................116
Adding an event .............................................................................................117
Responding to events.....................................................................................121
Customizing system notifications................................................................126

CHAPTER 8 Using UDP connections...............................................................................131


UDP connections ............................................................................................132
Using UDP connections.................................................................................132

CHAPTER 9 Using SMS ....................................................................................................135


SMS overview .................................................................................................136
Opening a network connection ....................................................................136
Sending an SMS message ..............................................................................137
Receiving an SMS message ...........................................................................139
Testing an SMS application...........................................................................141

CHAPTER 10 Managing applications ...............................................................................143


Working with the application manager ......................................................144
Working with code modules ........................................................................148
Managing code modules ...............................................................................149

CHAPTER 11 Communicating with other applications ..................................................151


Runtime store ..................................................................................................152
Adding runtime objects .................................................................................152
Retrieving runtime objects ............................................................................153
APPENDIX A Sample code ................................................................................................155
Base class for UI applications .......................................................................156
Persistence sample application.....................................................................158

APPENDIX B Format of .alx files.......................................................................................173


About .alx files ................................................................................................174
Elements in .alx files.......................................................................................176

Acronym list.................................................................................................179

Index.............................................................................................................183

Code samples
BasicMail.java ...........................................................................................................33
ContactsDemo.java ..................................................................................................51
TaskDemo.java .........................................................................................................58
EventDemo.java .......................................................................................................65
DemoOptionsProvider.java ...................................................................................76
UserInfo.java ............................................................................................................86
Restaurants.java .......................................................................................................92
RestaurantsSync.java ............................................................................................107
NotificationsDemo.java ........................................................................................123
ConsesquenceDemo.java ......................................................................................129
SendSms.java ..........................................................................................................138
ReceiveSms.java .....................................................................................................140
BaseApp.java ..........................................................................................................156
MoreRestaurants.java ...........................................................................................159
MenuHelper.java ...................................................................................................168
CookieMenuItem.java ...........................................................................................171
About this guide
This section provides information on the following topics:
• Overview
• Audience
• Conventions
• Related resources
About this guide

Overview
This document explains advanced topics in creating wireless applications using the BlackBerry™
solution, including the following topics:
• integrating email and personal information management (PIM)
• working with UDP connections and short message service (SMS)
• storing persistent data on the handheld
• performing backup and restore operations on application data
• using the runtime store to communicate with other applications

Product compatibility
Unless otherwise noted, the information in this book applies to the following BlackBerry products:

Product Version

BlackBerry Java Development Environment (JDE) 3.6 or later

BlackBerry handheld software 3.6 or later

BlackBerry Enterprise Server for Microsoft® Exchange 3.5 or later

BlackBerry Enterprise Server for Lotus® Domino™ 2.2 or later

The APIs that are described in this guide are available only with the BlackBerry JDE version 3.6.
Applications that are written using the BlackBerry JDE version 3.6 can only be run on BlackBerry
handheld software version 3.6 or later.
The BlackBerry Enterprise Server version 2.1 or earlier for Lotus Domino and the BlackBerry
Enterprise Server version 2.1 or earlier for Microsoft Exchange do not include the Mobile Data
Service feature, so they do not support client/server applications in a corporate environment.

Audience
This guide is intended for Java developers who want to create Java™ client applications for the
BlackBerry Wireless Handheld™.
This guide assumes that you are already familiar with wireless Java programming for the
BlackBerry solution, and have read the BlackBerry Java Developer Guide Volume 1.

10 BlackBerry Java Development Environment


About this guide

Conventions
Warning: Warnings advise you that failure to take or avoid a specific action could result in data loss or physical
damage.

Note: Notes contain important information on the associated topic.

Tip: Tips provide optional or time-saving information on the associated topic.

Related resources
These other resources can help you to develop wireless applications for BlackBerry.

Related documentation
• BlackBerry Java Developer Guide Volume 1 – Fundamentals
Volume 1 provides information on the BlackBerry solution, application design considerations,
and development tools, as well as information on BlackBerry APIs for user interfaces,
localization, and networking.
• IDE Online Help
The IDE Online Help provides detailed procedures for using the integrated development
environment (IDE). To view the IDE Online Help in the IDE, on the Help menu, click IDE Help.
Use the table of contents, index, or full-text search to find specific information.
• API reference documentation
For detailed descriptions of APIs that are provided with the BlackBerry Java Development
Environment (JDE), refer to the online API reference documentation. To view API reference
documentation in the IDE, on the IDE Help menu, click API Reference.
• BlackBerry Browser Developer Guide
This guide explains how to create web content and web-based applications using the
BlackBerry Browser.

Developer support
Visit the BlackBerry Developer Zone at http://www.blackberry.com/developers for additional
documentation, technical updates, tutorials, and a developer discussion forum.

Developer Guide 11
About this guide

12 BlackBerry Java Development Environment


Chapter 1
Using controlled APIs
This section provides information on the following topics:
• Controlled APIs
• About code signatures
• Registering for code signing
• Requesting code signatures
Chapter 1: Using controlled APIs

Controlled APIs
This guide provides information on BlackBerry APIs for which access is controlled. You can run
applications that use controlled APIs in the simulator; however, you must obtain code signatures
from Research In Motion before your application can be loaded onto a BlackBerry Wireless
Handheld. Refer to "About code signatures" on page 15 for more information.

Runtime APIs
This guide provides information on the following runtime APIs that require code signatures. Refer
to the BlackBerry Developer Guide Volume 1 – Fundamentals for information on other runtime APIs.

Package Description

net.rim.device.api.crypto The cryptographic APIs provide data security capabilities, including


net.rim.device.api.crypto.* data encryption and decryption, digital signatures, data
authentication, and certificate management.

net.rim.device.api.notification The notifications API provides methods to trigger event notifications


and respond to system-wide and application-specific events.

net.rim.device.api.synchronization The synchronization API enables applications to perform backup and


restore operations on custom data.

net.rim.device.api.system Classes in this package with controlled access provide functionality


such as persistent data storage, interprocess communication (IPC),
SMS, network communication using datagrams, and application
management.

14 BlackBerry Java Development Environment


About code signatures

BlackBerry application APIs


The following APIs enable applications to interact with standard BlackBerry applications.

Package Description

net.rim.blackberry.api.mail The mail API enables applications to interact with the BlackBerry
net.rim.blackberry.api.mail.event messages application to send, receive, and open email messages.

net.rim.blackberry.api.menuitem The menu item API enables applications to add custom menu items to
BlackBerry applications, such as the address book, calendar, and
messages.

net.rim.blackberry.api.pim The PIM API enables applications to interact with BlackBerry personal
information management (PIM) applications, including address book,
tasks, and calendar.

net.rim.blackberry.api.options The options API enables applications to add handheld option items to
the handheld Options application.

About code signatures


RIM must track the use of some sensitive APIs in the BlackBerry JDE for security and export
reasons. In the API reference documentation, sensitive classes or methods are indicated by a lock
icon or are noted as “signed.” In the documentation for a class that contains signed methods, you
can select or clear the SHOW Signed option at the top of the page to view or hide signed methods.
If you use these classes or methods in your applications, the .cod files must be digitally signed by
Research In Motion before you can load the .cod files onto the BlackBerry Wireless Handheld.

Note: In the simulator, you can run applications that use sensitive APIs without .cod files being signed so that
you can test and debug your code. Code must be signed only when you are ready to deploy an application to
the handheld.

Use the signature tool, which is installed with the BlackBerry JDE, to request the appropriate
signatures for your .cod files.

Note: You never send your actual code to RIM. The signature tool sends a SHA-1 hash of your code file so that
the signing authority system can generate the necessary signature.

Developer Guide 15
Chapter 1: Using controlled APIs

Code signature verification


There are two types of code signature verification:
• Linktime verification: When you load a signed .cod file onto the handheld, the virtual machine
(VM) links the .cod file with the API libraries and verifies that the .cod file includes the required
signatures. If a signature is missing, the VM stops linking and does not load the application.
• Runtime verification: When the user uses the application on the handheld, if the application
invokes a method that requires a signature, the VM verifies that the application contains the
necessary signature. If the signature is not present, a ControlledAccessException is thrown and
the requested operation is not performed.

Registering for code signing


Register with the signing authority system to activate your account.

Register with the signing authority


Note: You must have HTTP access to the Internet to register.

1. Complete the registration form on the BlackBerry Developer Zone at


http://www.blackberry.com/developers.

RIM calls you to give you a registration personal information number (PIN), and sends you an
email message with a code signing identification (.csi) file attached.
2. Save the .csi file to your computer.
3. Double-click the .csi file.
If a dialog box appears that states that a private key cannot be found, complete these steps
before you continue:
• Click Yes to create a new key pair file. A dialog box appears.
• Type a password for your private key, and type it again to confirm.
• Click OK. A prompt appears.
• Move your mouse to generate data for a new private key.

16 BlackBerry Java Development Environment


Registering for code signing

The Signature Tool Registration window appears.

Signature Tool Registration window


4. In the Registration PIN field, type the client PIN that RIM provided.
5. In the Private Key Password field, type a password of at least eight characters. This is your
private key password, which protects your private key.

Note: Protect your private key password. If you lose this password, you must egister again with RIM. If this
password is stolen, contact RIM immediately to revoke your key to prevent others from requesting code
signatures using your identity.

6. Click Register. The registration request is sent to the RIM signing authority. A dialog box
appears to confirm your registration.
7. Click Exit.

Developer Guide 17
Chapter 1: Using controlled APIs

Change your private key password


Note: You must have HTTP access to the Internet to change your private key password.

1. In the BlackBerry JDE bin folder, double-click SignatureTool.jar. The Signature Tool window
appears.
2. Click Change Password. A dialog box appears.

Verify Old Private Key Password dialog box


3. In the Old Password field, type your current private key password.
4. Click Verify. If you typed the correct password, the Change Private Key Password dialog box
appears.

Change Private Key Password dialog box


5. In the Please enter your new password field, type a new password.
6. In the Confirm your new password field, type your new password again.
7. Click OK. Your private key password is changed.

18 BlackBerry Java Development Environment


Requesting code signatures

Requesting code signatures


To deploy your application to users, use the Signature Tool to obtain the necessary code signatures.

Open the Signature Tool


1. Build your projects. In the IDE, on the Build menu, click Build. The IDE creates three files for
each project in the simulator folder:
• .cod file: the compiled project that is loaded on the handheld
• .csl file: a list of linktime signatures that are required for the corresponding .cod file
• .cso file: a list of optional signatures that might be required at runtime if the application
invokes controlled methods
2. In the BlackBerry JDE bin folder, double-click SignatureTool.jar. The Open window appears.

Signature Tool – Open window


3. In the Look In drop-down menu, select the folder in which the .cod file is located.
4. Select a .cod file for which to request signatures. Hold the CTRL key to select multiple .cod files.

Developer Guide 19
Chapter 1: Using controlled APIs

5. Click Open. The selected .cod files are added to the list.

Signature Tool window – .cod files selected for signature request


6. Click Add to select additional .cod files for which to request signatures.
7. Click Request. A dialog box appears.
8. In the field, type your private key password.
9. Click OK. The signing request process occurs as follows:
• The Signature Tool opens an HTTP connection to the signing authority system and sends a
request. The request includes a hash of your code in the .csl and .cso files. Your actual code
is not sent to RIM.
• The signing authority system verifies that the request is valid and applies a RIM private key
to the hash of each .cod file to create the signatures.
• The signing authority system returns the signatures to the Signature Tool and closes the
HTTP connection.
• The Signature Tool appends the signatures to each .cod file.

20 BlackBerry Java Development Environment


Requesting code signatures

When the files are signed, the Status column for the .cod file displays Signed.

Signature Tool window – after .cod files signed


If any problems occur with the signature request, the Status column displays
Failed - See Details. Refer to the Details column for more information.
When your .cod files have the appropriate signatures applied, you can load them onto the
BlackBerry Wireless Handheld. Refer to the BlackBerry Java Developer Guide Volume 1 – Fundamentals
for information on deploying applications.

Developer Guide 21
Chapter 1: Using controlled APIs

22 BlackBerry Java Development Environment


Chapter 2
Integrating email
This section provides information on the following topics:
• BlackBerry Mail API
• Working with messages
• Working with folders
• Working with attachments
• Testing an application in the simulator
• Deploying an application
Chapter 2: Integrating email

BlackBerry Mail API


The BlackBerry Mail API (net.rim.blackberry.api.mail and net.rim.blackberry.mail.event
packages) enables you to write applications that can send and receive email messages.

Note: The mail API provides access to email messages in the handheld Messages list, but not to other messages,
such as SMS messages or phone call logs.

Email services
The Session class, which represents an abstract model for email operations, provides access to email
service, storage, and transport. An application retrieves a new Session object to send or receive
email messages. Typically, you can invoke Session.waitForDefaultSession() to retrieve a session
with the default mail service on the user’s handheld and wait until the service is available.
Alternatively, you can create a ServiceConfiguration object for a particular email service and
invoke Session.getDefaultInstance(ServiceConfiguration) to retrieve a session.
Use the Session instance to retrieve a Store instance. The Store class models the underlying
message storage on the handheld.
The Transport class represents the email transport protocol.

Messages
The Message class represents an email message. A Message object consists of a set of attributes and a
message body (its contents). Refer to "Multipart messages" on page 25 for more information on
multipart messages.
Message attributes include the sender, recipients, subject, and status, as well as MIME header fields.
The Message class contains four interfaces:
• Message.Status: defines status options for sending and receiving messages, such as
RX_RECEIVED, RX_ERROR, TX_SENT, and TX_READ

• Message.RecipientType: defines supported recipient types, such as TO, CC, or BCC

• Message.Flag: defines message flags, such as MOVED, OPENED, or SAVED

• Message.Icons: defines characters that indicate the status of a message, such as a check mark
for a sent message
The Address class represents an email address that is used in the from, reply-to, and recipient
attributes, and in the message body. The Address class contains fields to store the fully qualified
address string, such as scott.tooke@rim.com, and the display name.
The Header class defines supported header fields, such as TO, FROM, and DATE.

24 BlackBerry Java Development Environment


BlackBerry Mail API

Multipart messages
The mail API supports multipart messages. The Multipart abstract class provides a container for
multiple BodyPart objects. Multipart provides methods to retrieve and set its subparts.
Each BodyPart consists of header fields (attributes) and contents (body). The mail API provides four
implementations of BodyPart:
• ContactAttachmentPart: an address card attachment part, using the
net.rim.blackberry.api.pim.Contact interface. Refer to "Address book" on page 45 for more
information.
• TextBodyPart: a body part with content that is text/plain type. You use this class to create a
multipart message that includes a text/plain part.
• UnsupportedAttachmentPart: an unsupported attachment part. You cannot instantiate this
class. The content type is always application/octet-stream.
• SupportedAttachmentPart: a supported attachment part, for which there is a registered
attachment handler on the handheld.

Message storage
The Folder class represents a local mailbox folder. Several types are defined for folders, including
INBOX, OUTBOX, SENT, and OTHER. You can use these folder types to retrieve folders for retrieving or
saving messages.
The Store class models the underlying message storage and provides methods for finding and
retrieving folders. Folders exist in a hierarchy, as the following example demonstrates:
Mailbox - Katie Laird
Inbox
Projects
In Progress
Complete
Personal

A standard delimiter character separates each folder in the hierarchy, which you can retrieve using
the getSeparator() method. You can list all the folders in a Store object, list the subfolders in a
folder, or find a folder based on a search string.
The Folder class defines methods for retrieving messages or subfolders, saving messages, and
deleting messages.

Note: Multiple Folder instances can refer to the same physical folder on the handheld. As a result, you should
always invoke addFolderListener() and removeFolderListener() on the same Folder object. You can use
the Folder.equals() method to determine whether two Folder objects refer to the same physical folder.

Developer Guide 25
Chapter 2: Integrating email

Mail events
The BlackBerry mail event package (net.rim.blackberry.api.mail.event) defines the following
messaging events, and listeners for each event:
• FolderEvent: a message in a folder is added or removed

• MessageEvent: a message changes (body, header, or flags)

• StoreEvent: a message is added to, or removed from, the message store in a batch operation (for
example, when the handheld is synchronized with the desktop, or when a user clicks the Delete
Prior menu item in the Messages screen)
MailEvent is the base class for these mail event classes. The MailEvent class defines an abstract
dispatch method to invoke the appropriate listener method for each event.

The EventListener interface provides a common interface for the FolderListener and
MessageListener interfaces. You can add a FolderListener to a Folder or Store object. You can add
a MessageListener to a Message object. You can add a StoreListener to a Store object.

Working with messages


Use the mail API to receive and open email messages on the handheld.

Receive message notification


Perform the following steps to receive notification of new messages.
1. Implement the FolderListener interface.

public class MailTest implements FolderListener, StoreListener {

2. Retrieve the Store object and add the StoreListener to it.

try {
Store store = Session.waitForDefaultSession().getStore();
} catch (NoSuchServiceException e) {
System.out.println(e.toString());
}

store.addStoreListener(this);

3. Retrieve the Folder object for which you want to receive notifications of new messages. This
example retrieves the first inbox folder.

Folder[] folders = store.list(Folder.INBOX);


Folder inbox = folders[0];

26 BlackBerry Java Development Environment


Working with messages

4. Add the FolderListener to this folder.

inbox.addFolderListener(this);

When a new email message arrives, a FolderEvent is generated and sent to the listener.
5. Implement the messagesAdded and messagesRemoved methods. These methods are defined by
the FolderListener interface.

void messagesAdded(FolderEvent e)
{
//perform processing on added messages
}

void messagesRemoved(FolderEvent e)
{
//perform processing on removed messages
}

These methods are invoked when a message is added to, or removed from, a folder. You can use
the messagesAdded and messageRemoved methods to maintain the consistency of any references
in your application to specific mail folders.
In addition, you might use the messageAdded method to notify the application of the receipt of a
specific email message. For example, in an order entry application, the application might expect
an email message to confirm that an order was accepted or rejected with a specific reason.
6. Implement the batchOperation method, which is defined by the StoreListener interface.

void batchOperation(StoreEvent e)
{
//perform action when messages added or moved in batch operation
}

The batchOperation() method is invoked when messages are added to, or removed from, the
message store in a batch operation. A batch operation can occur when the handheld is
synchronized with the desktop. For example, your application could check if any messages were
removed to which it has references.

Developer Guide 27
Chapter 2: Integrating email

Receive more of a message


By default, the first portion of a message (typically about 2 KB) is sent to the handheld. Invoke
hasMore() on a body part to determine if more data is available on the server. Invoke
moreRequestSent() to determine if a request for more data has already been sent.

The following example demonstrates how to retrieve more of body part (bp).
if (( bp.hasMore() ) && (! bp.moreRequestSent())
{
Transport.more(bp, true);
}

The second parameter of the more() method is a Boolean value that specifies whether to retrieve
only the next section of the body part (false) or all remaining sections of the body part (true).

Open messages
Complete the following steps to enable an application to open messages in a folder.
1. Retrieve a Store object.

Store store = Session.waitForDefaultSession.getStore();

2. Retrieve the message objects from the folder.

Folder folder = Store.getFolder("SampleFolder");

You can also use the findFolder method to return an array of all folders in the current hierarchy
that match a specified string:
Folder[] folders = Store.findFolder("Sample");

3. Retrieve the message objects from the folder.

Message[] msgs = folder.getMessages();

You can then iterate through the array and retrieve information, such as the sender and subject,
to display to the user.
4. When the user selects a message from the list, you can invoke methods on the Message object to
retrieve the appropriate fields and body contents to display to the user. This example retrieves
the first message.

Message msg = msgs[0];


Address[] recipients = msg.getRecipients(Message.RecipientType.TO)
Date sent = msg.getSentDate();
Address from = msg.getFrom();
String subject = msg.getSubject();
Object o = msg.getContent();

28 BlackBerry Java Development Environment


Working with messages

//verify that the message is not multipart


if ( o instanceof String )
{
String body = (String)o;
}
...

Tip: Invoke getBodyText() on a message to retrieve plain text contents as a String. If the message does not
contain plain text, the method returns null.

Send a message
You send messages using a Transport object, which represents the email transport protocol. The
Transport class provides the send() method for sending an email message.

1. Create a Message object, and specify a folder in which to save a copy of the sent message.

Store store = Session.getDefaultInstance().getStore();

Folder[] folders = store.list(Folder.SENT);


Folder sentfolder = folders[0];

Message msg = new Message(sentfolder);

2. Specify the recipients of the message. Create an array of Address objects and add each address to
the array. You should catch AddressException, which is thrown if an address is invalid.

Address toList[] = new Address[1];

try {
toList[0]= new Address("scott.tooke@rim.com", "Scott Tooke");
} catch(AddressException e) {
System.out.println(e.toString());
}

3. Add recipients by invoking Message.addRecipients(). As parameters to this method, provide


the type of recipient (TO, CC, or BCC) and the array of addresses to add.

msg.addRecipients(Message.RecipientType.TO, toList);

Developer Guide 29
Chapter 2: Integrating email

4. Add CC or BCC recipients in the same way that you added TO recipients.

Address ccList[] = new Address[1];


try {
ccList[0]= new Address("katie.laird@rim.com", "Katie Laird");
} catch(AddressException e) {
System.out.println(e.toString());
}
msg.addRecipients(Message.RecipientType.CC, ccList);

5. Add the name and email address of the message sender.

Address from = new Address("scott.tooke@rim.com", "Scott Tooke");


msg.setFrom(from);

6. Add a subject line to the message.

msg.setSubject("Test Message");

7. Add the content of the message. Typically, you retrieve content from text that a user types in a
field.

try {
msg.setContent("This is a test message.");
} catch(MessagingException e) {
System.out.println(e.getMessage());
}

8. Send the message by invoking the static Transport.send() method.

try {
Transport.send(msg);
} catch(MessagingException e) {
System.out.println(e.getMessage());
}

Reply to a message
To create a message as a reply to an existing message, invoke Message.reply(). As a parameter to
this method, specify true to reply to all message recipients or false to reply only to the sender.
The following example demonstrates how to reply to a message. This example retrieves the first
message in the Inbox.
Store store = Session.waitForDefaultSession().getStore();

Folder[] folders = store.list(INBOX);


Folder inbox = folders[0];
Message[] messages = folder.getMessages();

30 BlackBerry Java Development Environment


Working with messages

if( messages.length > 0 ) {


Message msg = messages[0];
}

Message reply = msg.reply(true);

Transport.send(reply);

Forward a message
1. Invoke forward() on an existing Message. The forward() method returns a new Message object.

Message fwdmsg = msg.forward();

2. Invoke methods on the new Message object to add recipients.

Address toList[] = new Address[1];


toList[0]= new Address("katie.laird@rim.com", "Katie Laird");
fwdmsg.addRecipients(Message.RecipientType.TO, toList);

3. Optionally invoke setContent() to add text before the forwarded message contents.

try {
fwdmsg.setContent("This is a forwarded message.");
} catch(MessagingException e) {
System.out.println(e.getMessage());
}

Note: The subject line of a forwarded message is set automatically to FW:<original_subject>. You cannot
edit the text of the forwarded message.

4. Invoke send() to send the forwarded message.

try {
Transport.send(fwdmsg);
} catch(MessagingException e) {
System.out.println(e.getMessage());
}

Developer Guide 31
Chapter 2: Integrating email

Working with folders


This section explains how to list, retrieve, and search for folders. You must retrieve a Store object:
Store store = Session.waitForDefaultSession().getStore();

List folders
You can list the folders in a mailbox store by invoking the Store.list() method.
Folder[] folders = store.list();

Retrieve folders by type


You can retrieve folders of a specific type. For example, to retrieve the user’s Inbox folder, you can
retrieve all folders of type INBOX and then use the first one. The following sample code demonstrates
how to do this:
Folder[] folders = store.list(INBOX);
Folder inbox = folders[0];

Retrieve folders using a search


You can retrieve all the folders in the hierarchy that match a specified search string, as in the
following example:
Folder[] folders = store.findFolder("Inbox");

The findFolder method returns an array of folders that match the specified string, or an empty
folder if a matching folder is not found.

Retrieve a folder by name


You can retrieve a folder by name by specifying the absolute path to the folder name. You must
catch the exception that is thrown if the folder is not found.
Folder f = null;
try {
f = store.getFolder("Mailbox - Scott Tooke/Inbox/Projects/Urgent");
} catch (FolderNotFoundException e) {
//handle exception
}

32 BlackBerry Java Development Environment


Working with folders

Retrieve a folder by ID
You can retrieve a folder by its unique ID. The following sample code demonstrates how to use the
getID() method to retrieve the folder ID, and then use that ID to retrieve the folder.

Folder[] folders = store.list();


long id = folders[0].getId();
Folder f2 = store.getFolder(id);

Save a message
You can save messages by invoking the appendMessage method on a Folder object. The following
code sample demonstrates how to do this.
Message msg = new Message();
...
Folder folder = store.getFolder("Inbox");

folder.appendMessage(msg);

Example: BasicMail.java

/**
* BasicMail.java
* Copyright (C) 2001-2003 Research In Motion Limited.
*/

import net.rim.blackberry.api.mail.*;
import net.rim.blackberry.api.mail.event.*;
import net.rim.device.api.system.*;

public class BasicMail extends Application {

private Store store;

static void main (String args[]) {


BasicMail app = new BasicMail();
app.enterEventDispatcher();
}

BasicMail() {

Store store = Session.getDefaultInstance().getStore();

Folder[] folders = store.list(Folder.SENT);


Folder sentfolder = folders[0];

Developer Guide 33
Chapter 2: Integrating email

//create message
Message msg = new Message(sentfolder);

//add TO Recipients
Address toList[] = new Address[1];
try {
toList[0]= new Address("scott.tooke@rim.com", "Scott Tooke");
} catch(AddressException e) {
System.out.println(e.toString());
}
msg.addRecipients(Message.RecipientType.TO, toList);

//add CC Recipients
Address ccList[] = new Address[1];
try {
ccList[0]= new Address("katie.laird@rim.com", "Katie Laird");
} catch(AddressException e) {
System.out.println(e.toString());
}
msg.addRecipients(Message.RecipientType.CC, ccList);

//add the subject


msg.setSubject("A Test Email");

//add the body


try {
msg.setContent("This is a test message.");
} catch(MessagingException e) {
//handle exception
}

//send the message


try {
Transport.send(msg);
} catch(MessagingException e) {
System.out.println(e.getMessage());
}

System.out.println("Email sent successfully.");


System.exit(0);
}
}

34 BlackBerry Java Development Environment


Working with attachments

Working with attachments


The BlackBerry API enables you to support inbound email attachments and to create attachments on
the handheld.

Implement AttachmentHandler
Your application must implement the AttachmentHandler interface.
1. Implement the supports(contentType) method to register the MIME type of attachments that
your attachment handler accepts. The supports() method is invoked when the handheld
receives an attachment.

public boolean supports(String contentType)


{
return (
contentType.toLowerCase().indexOf("contenttype") != -1 ? true : false);
}

2. Implement the menuString() method to return the menu item string to display on the Messages
screen when the user selects an attachment.

public String menuString()


{
return "Custom Attachment Viewer";
}

3. Implement the run() method to perform the appropriate processing on the attachment data and
display it to the user.

public void run(Message m, SupportedAttachmentPart p)


{
//perform processing on data
Screen view = new Screen();
view.setTitle(new LabelField("Attachment Viewer"));
view.add(new RichTextField(new String((byte[])p.getContent())));
}

Note: The run() method is invoked when the corresponding menu item is selected on the Messages screen.

Developer Guide 35
Chapter 2: Integrating email

Register an attachment handler


The AttachmentHandlerManager class controls how attachments are processed on the handheld. You
can register your AttachmentHandler instance so that it is invoked when the user opens an
attachment of the associated type.

Note: Built-in attachment service support on the BlackBerry Wireless Handheld hsa first priority in receiving
attachments. Third-party attachment handlers cannot override default handheld behavior.

1. Retrieve an AttachmentHandlerManager instance.

AttachmentHandlerManager m = AttachmentHandlerManager.getInstance();

2. Invoke the addAttachmentHandler method.

CustomAttachmentHandler ah = new CustomAttachmentHandler();


m.addAttachmentHandler(ah);

Retrieve attachment contents


Invoke the getContent() method to retrieve the contents.
public void run(Message m, SupportedAttachmentPart p)
{
...
String s = new String((byte[])p.getContent());
}

Retrieve attachment information


You can invoke several methods on a SupportedAttachmentPart to retrieve information, such as the
attachment name or size.
public void run(Message m, SupportedAttachmentPart p)
{
...
String name = p.getName();
int size = p.getSize();
}

36 BlackBerry Java Development Environment


Working with attachments

Write to an output stream


Invoke the writeTo method to write the name and data of an attachment to an output stream.
public void run(Message m, SupportedAttachmentPart p)
{
...
OutputStream os = new OutputStream();
try {
p.writeTo(os);
} catch(IOException e) {
//an I/O error has occurred
}
}

Send an attachment
1. Create a Message object.

Message msg = new Message();


Address[] addresses = new Address[1];
Address a = new Address("scott.tooke@rim.com", "Scott Tooke");
addresses[0] = a;
msg.addRecipients(Message.RecipientType.TO, addresses);
msg.setSubject("Test message with attachments");

Refer to "Send a message" on page 29 for more information.


2. Create a byte array of data to add to the attachment.

byte[] buf = new byte[256];

3. Create a MultiPart object.

MultiPart mp = new MultiPart();

The default MultiPart constructor creates a MultiPart object with a type of multipart/mixed.
4. Create an attachment part by invoking the SupportedAttachmentPart constructor.

SupportedAttachmentPart attach = new SupportedAttachmentPart(


mp, "application/x-example", "filename", data);

5. Add the attachment part to a MultiPart.

mp.addBodyPart(attach);

Developer Guide 37
Chapter 2: Integrating email

6. Set the MultiPart object as the contents of the message.

msg.setContent(mp);

7. Send the message, which now includes the attachment.

Transport.send(msg);

Testing an application in the simulator


The BlackBerry JDE includes an email server simulator (ESS) that enables you to send and receive
email between the handheld simulator and either a desktop email application, such as Microsoft®
Outlook Express, or POP3 and SMTP servers. A BlackBerry Enterprise Server is not required.
1. On the Windows Start menu, select Programs > Research In Motion > BlackBerry Java
Development Environment 3.6 > Email Server Simulator. The simulator window appears.

Email Server Simulator window


2. Select one of the following modes:
• Standalone mode: The ESS stores messages on the local file system and communicates
directly with a desktop email application. No POP3 or SMTP server is required.
The ESS can communicate with any desktop email application that supports POP3 and
SMTP, such as Microsoft Outlook Express. The desktop email account must have the POP3
server set to localhost on port 110 and the SMTP server set to localhost on port 25.
• Connected mode: The ESS polls the user's POP3 email server for incoming messages, and
uses the user's SMTP server to send messages. Valid POP3 and SMTP servers are required.

38 BlackBerry Java Development Environment


Testing an application in the simulator

3. If you select Standalone mode, click Clean FS to erase ESS messages that are stored on the local
file system.
4. If you select Connected mode, type information in the following fields:
• Outgoing: host name of the SMTP server that your email account uses
• Incoming: host name of the POP3 server that your email account uses
• User name: user name with which to connect to your email account
• Password: password with which to connect to your email account
• Poll inbox: specifies, in seconds, how often the simulator checks your email inbox for new
messages
5. Type information in the following fields:
• Name: name to display in outgoing messages from the handheld simulator
• Email: email address to display in outgoing messages from the handheld simulator
• PIN: personal information number (PIN) that is used by the handheld simulator (default
PIN is 0x2100000A)
6. Click Launch. The email simulator starts.
If you changed parameter values in the ESS window, a dialog box appears. Click Yes to save
changes. Click No to use the parameter values for this time only.
7. Check the command prompt window for detailed information on ESS startup, including any
login errors.
After the ESS starts, you can use applications in the handheld simulator to send and receive
email messages with a desktop email account. Refer to IDE Online Help or
BlackBerry Java Developer Guide Volume 1 – Fundamentals for information on using the simulator.

Developer Guide 39
Chapter 2: Integrating email

Deploying an application
The BlackBerry Mail API is not installed on the handheld by default, but it is provided with the
BlackBerry Desktop Software. Your application can load the API when it is loaded.
1. In the IDE, select the project for your application.
2. On the Project menu, click Generate .alx File. An .alx file for the project is output to folder in
which the .jdp project file is located.
3. Edit the .alx file to specify that your application is dependent on the BlackBerry Mail API.
Add the <requires id="net.rim.blackberry.api.mail"> tag inside the <application> tag:

<application id="sampleapp">
<requires id="net.rim.blackberry.api" />
<name>Sample application</name>
...

Refer to "Format of .alx files" on page 173 for more information.


When a user loads your application, the BlackBerry mail API is also loaded if it is not already on
the user’s handheld.

Note: The .cod files for the BlackBerry Mail API that are included with the desktop software can change between
release of the desktop software and release of the BlackBerry JDE. In this case, you must redistribute updated API
.cod files with your application. Installation packages will be provided on the BlackBerry Developer Zone web
site. Refer to the BlackBerry JDE release notes for more information.

40 BlackBerry Java Development Environment


Chapter 3
Integrating PIM
functions
This section provides information on the following topics:
• PIM API overview
• Address book
• Tasks
• Calendar
• Deploying your application
Chapter 3: Integrating PIM functions

PIM API overview


The BlackBerry personal information management (PIM) APIs enable you to access the calendar,
tasks, and address book on the handheld. The PIM class is an abstract class that provides methods for
accessing PIM databases on the handheld. Invoke PIM.getInstance() to retrieve a PIM object.
The APIs are provided in the net.rim.blackberry.api.pim package.

PIM lists
The PIMList interface represents common functionality of all contact, event, or task lists. A list
contains zero or more items, represented by subclasses of PIMItem. Use PIM lists to organize related
items and to retrieve some or all of the items in the list.

PIMList interfaces

Note: On the BlackBerry Wireless Handheld, each ContactList, ToDoList, or EventList instance refers to the
native database on the handheld. Third-party applications cannot create custom lists.

42 BlackBerry Java Development Environment


PIM API overview

PIM items
The PIMItem interface represents the common functionality of an item in a list. The specific Contact,
Event, and ToDo interfaces extend PIMItem. A PIM item represents a collection of data for a single
entry, such as a calendar appointment or a contact.

PIMItem interfaces
When you create a PIM item in a particular PIM list, the item remains associated with that list as
long as it exists. You can also import or export data in PIM items using standard formats, such as
iCal and vCard.

Fields
A PIM item stores data in fields. Each PIMItem interface—Contact, Event, or ToDo—defines unique
integer IDs for each field that it supports. For example, the Contact interface defines fields to store
an email address (EMAIL), name (FORMATTED_NAME), and phone number (TEL).
Each field has a data type ID, such as INT, BINARY, DATE, BOOLEAN, or STRING. To retrieve the data type
of a field, invoke PIMList.getFieldDataType(int field). The data type determines which methods
to use to retrieve or set field data. For example, if a field’s data type is STRING, invoke
PIMItem.addString() to add a value, PIMItem.setString() to change an existing value, and
PIMItem.getString() to retrieve a value.

Before you attempt to set or retrieve a field value, verify that the item supports the field by invoking
PIMList.isSupportedField( int field ).

A field can have an associated descriptive label to display to users. To retrieve a field label, invoke
PIMList.getFieldLabel(int field).

Developer Guide 43
Chapter 3: Integrating PIM functions

Listening for changes


An application can implement the PIMListListener interface to receive notification when an item in
a list changes. The PIMListListener interface provides three methods:
• itemAdded(PIMItem item) is invoked when an item is added to a list

• itemRemoved(PIMItem item) is invoked when an item is removed from a list

• itemUpdated(PIMItem oldItem, PIMItem newItem) is invoked when an item changes

To add the listener, cast a list to a BlackBerryPIMList object and invoke the addListener() method.
The following example demonstrates how to add a PIMListListener to a BlackBerryPIMList and
implement listener methods.
ContactList cl = (ContactList)PIM.getInstance().openPIMList(
PIM.CONTACT_LIST, PIM.WRITE_ONLY);

((BlackBerryPIMList)cl).addListener(new PIMListListener() {
public void itemAdded(PIMItem item) {
System.out.println(" ITEM ADDED: " +
((Contact)item).getString(Contact.UID, 0));
}

public void itemUpdated(PIMItem oldItem, PIMItem newItem) {


System.out.println(" ITEM UPDATED: " +
((Contact)oldItem).getString(Contact.UID, 0) + " to " +
((Contact)newItem).getString(Contact.UID, 0));
}

public void itemRemoved(PIMItem item) {


System.out.println(" ITEM REMOVED: " +
((Contact)item).getString(Contact.UID, 0));
}
});

Note: The listener remains associated with the handheld database even after the correspoding PIMList object
has been deleted. To remove the listener, invoke BlackBerryPIMList.removeListener().

44 BlackBerry Java Development Environment


Address book

Address book
Use an instance of the ContactList class to add or view contact information in the handheld
address book. Create Contact objects to store individual address book entries with information such
as name, telephone number, email address, and street address.
The BlackBerryContact interface, which extends Contact, defines the following constants to
provide access to fields that are specific to BlackBerry contacts:
• BlackBerryContact.PIN provides access to the PIN field

• BlackBerryContact.USER1 through USER4 provides access to the USER1 through USER4 fields

Invoke BlackBerryPIMList.setFieldLabel() to define labels for the USER1 through USER4 fields.
Changing a label affects all contacts on the handheld. For example:
BlackBerryPIMList.setFieldLabel(BlackBerryContact.USER1, "A Custom Label");

The change takes affect immediately; a commit of the contact is not required.

Open a ContactList
You must create a contact list before you can add contacts. Invoke the PIM.openPIMList method,
and specify the type of list to open (PIM.CONTACT_LIST) and the access mode with which to open the
list (READ_WRITE, READ_ONLY, or WRITE_ONLY).
ContactList contactList = null;
try {
contactList = (ContactList)PIM.getInstance().openPIMList(
PIM.CONTACT_LIST, PIM.READ_WRITE);
} catch (PimException e) {
return;
}

Create a contact
To create a Contact item, invoke the createContact() method on a contact list.
Contact contact = contactList.createContact();

Note: The contact is not added to the database until you commit it. Refer to "Save a contact" on page 48 for more
information.

Developer Guide 45
Chapter 3: Integrating PIM functions

Add contact information


The Contact class defines fields in which to store data, such as Contact.NAME, Contact.ADDR, and
Contact.TEL. Each field has a specific data type, which you can retrieve by invoking
PIMList.getFieldDataType(int field). Depending on the data type of the field, add a new value a
by invoking one of the following methods: addString(), addStringArray(), addDate(), addInt(),
addBoolean(), or addBinary()

Before you attempt to set or retrieve a field, verify that the item supports the field by invoking
ContactList.isSupportedField( int field ).

Some fields can store multiple values, using attributes to differentiate between values. For example,
the TEL field supports the ATTR_HOME, ATTR_WORK, ATTR_MOBILE, and ATTR_FAX attributes to store
numbers for work, home, mobile, and fax numbers. To determine how many values a field
supports, invoke PIMList.maxValues(int field). This method returns the number of values
supported, or -1 to indicate that an arbitrary number of values can be added. To verify that a field
supports a particular attribute, invoke isSupportedAttribute(int field, int attribute).
The following code demonstrates how to add fields to a contact:
//create string array for name
try {
String[] name = new String[7]; //seven possible values in name array
name[Contact.NAME_PREFIX] = "Mr."
name[Contact.NAME_FAMILY] = "Tooke";
name[Contact.NAME_GIVEN] = "Scott";
name[Contact.NAME_FORMATTED] = "Scott Tooke";
} catch (IllegalArgumentException iae) {
}

//add name
if(contactList.isSupportedField(Contact.NAME) {
contact.addStringArray(Contact.NAME, Contact.ATTR_NONE, name);
}

//create string array for address


try {
String[] address = new String[7];
address[Contact.ADDR_COUNTRY] = "United States";
address[Contact.ADDR_LOCALITY] = "Los Angeles";
address[Contact.ADDR_POSTALCODE] = "632300";
address[Contact.ADDR_REGION] = "California";
address[Contact.ADDR_STREET] = "323 Main Street";
} catch (IllegalArgumentException iae) {
}

//add address
contact.addStringArray(Contact.ADDR, Contact.ATTR_NONE, address);

46 BlackBerry Java Development Environment


Address book

//add home telephone number


if (contactList.isSupportedField(Contact.TEL) &&
(contactList.isSupportedAttribute(Contact.TEL, TYPE_HOME)) {
contact.addString(Contact.TEL, Contact.TYPE_HOME, "111-555-2222");
}
//add work telephone number
if (contactList.isSupportedField(Contact.TEL)) {
contact.addString(Contact.TEL, Contact.TYPE_HOME, "613-542-2434");
}

//add work email address


if (contactList.isSupportedField(Contact.EMAIL)) {
contact.addString(Contact.EMAIL, Contact.TYPE_WORK,
"scott.tooke@rim.com");
}

//add a handheld PIN


if (contactList.isSupportedField(BlackBerryContact.PIN)) {
contact.addString(BlackBerryContact.PIN, Contact.ATTR_NONE, "99999999");
}

Change contact information


For fields that support only one value, invoke the appropriate set method, such as setString(), to
replace an existing value with a new value.

Note: If you invoke an add method, such as addString(), for a field that already has a value, a
FieldFullException is thrown. Use the corresponding set method, such as setString(), to change an
existing value.

For the name and address fields, which contain a string array value, you can retrieve the array and
then modify one or more indexes in the array before adding it back in. For example, the following
code changes the prefix to “Dr.” and adds a suffix, “Jr.”.
if (contact.countValues(Contact.NAME) > 0) {
String[] newname = contact.getStringArray(Contact.NAME, 0);
}

newname[Contact.NAME_PREFIX] = "Dr.";
newname[Contact.NAME_SUFFIX] = "Jr.";

contact.setStringArray(Contact.NAME, 0, Contact.ATTR_NONE, newname);

Developer Guide 47
Chapter 3: Integrating PIM functions

For fields that support multiple values, you can verify that the maximum number of values is not
exceeded before adding another value. For example, the following code demonstrates how to add
an email address for an existing contact:
if (contact.countValues(Contact.EMAIL) <
contactList.maxValues(Contact.EMAIL)) {
contact.addString(Contact.EMAIL, Contact.ATTR_NONE,
"katie.laird@rim.com");
}

Save a contact
Invoke the commit() method to save a contact. You can invoke isModified() to determine whether
any of the item’s fields have changed since the item was last saved:
if(contact.isModified()) {
contact.commit();
}

Remove a contact
To delete a Contact item, invoke the removeContact() method on a contact list.
contactList.removeContact(contact);

Retrieve contact information


To retrieve information for a contact in the address book, invoke PIMList.items() to enumerate
through the list.

Note: When you invoke PIMList.items() to retrieve an Enumeration of items in a list, the order of items is
undefined. Your application must sort items as necessary.

For a particular contact, invoke PIMItem.getFields() to retrieve an array of IDs for fields that have
data. Invoke PIMItem.getString() to retrieve the field values.

48 BlackBerry Java Development Environment


Address book

The following example demonstrates to how to retrieve information for contacts in the address book
and print values for fields with string data:
ContactList contactList = (ContactList)PIM.getInstance().openPIMList(
PIM.CONTACT_LIST, PIM.READ_WRITE);
Enumeration e = contactList.items();
while (e.hasMoreElements()) {
Contact c = (Contact)e.nextElement();
int[] fieldIds = c.getFields();
int id;
for(int index = 0; index < fieldIds.length; ++index) {
id = fieldIds[index];
if(c.getPIMList().getFieldDataType(id) == STRING) {
for(int j=0; j < c.countValues(id); ++j) {
String value = c.getString(id, j);
System.out.println(c.getFieldLable(id) + "=" + value);
}
}
}

Convert to serial formats


You can import data to, or export data from, PIM items using standard formats, such as vCal and
vCard. Invoke PIM.supportedSerialFormats() and specify the list type (PIM.Contact_LIST) to
retrieve a string array of supported formats.
Invoke toSerialFormat(PIMItem item, java.io.OutputStream os, java.lang.String enc,
java.lang.String dataFormat) to write an item to a supported serial format.

Note: The enc parameter specifies the character encoding to use when writing to the output stream. Supported
character encodings include "UTF8," "ISO-8859-1," and "UTF-16BE." This parameter cannot be null.

The following example demonstrates how to export all the contacts from the handheld address book
to a supported serial format, using an output stream writer:
ContactList contactList = (ContactList)PIM.getInstance().openPIMList(
PIM.CONTACT_LIST, PIM.READ_ONLY);

String[] dataFormats = PIM.getInstance().supportedSerialFormats(


PIM.CONTACT_LIST);
ByteArrayOutputStream bs = new ByteArrayOutputStream();
Enumeration e = contactList.items();
while (e.hasMoreElements()) {
Contact c = (Contact)e.nextElement();
PIM.getInstance().toSerialFormat(c, bs, "UTF8", dataFormats[0]);
}

Developer Guide 49
Chapter 3: Integrating PIM functions

Import contacts
To import data to the handheld address book, invoke the fromSerialFormat(java.io.InputStream
is, java.lang.String enc ) method.

Note: The enc parameter specifies the character encoding to use when writing to the output stream. Supported
character encodings include "UTF8," "ISO-8859-1," and "UTF-16BE." This parameter cannot be null.

You can then invoke the ContactList.importContact() method to add a new contact.
The following example demonstrates how to convert an existing Contact (contact) into a vCard and
then import the vCard into a new Contact.
String[] dataFormats = PIM.contactSerialFormats();

//write contact to vCard


ByteArrayOutputStream os = new ByteArrayOutputStream();
PIM.getInstance().toSerialFormat(contact, os, "UTF8", dataFormats[0]);

//import contact from vCard


ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray))));
PIMItem[] pi = PIM.getInstance().fromSerialFormat(istream, "UTF8");

ContactList contactList = (ContactList)PIM.getInstance().openPIMList(


PIM.CONTACT_LIST, PIM.READ_WRITE);

Contact contact2 = contactList.importContact((Contact)pi[0]);

Tip: The importContact() method saves the contact, so you do not have to invoke commit().

Close a ContactList
Close the ContactList when you finish using it.
try {
contactList.close();
} catch(PIMException ex) {
Dialog.alert(ex.toString());
}

The following example demonstrates how to display a screen that enables the user to add a new
contact to the handheld address book. After you save a contact, open the address book to verify that
the contact was saved.

50 BlackBerry Java Development Environment


Address book

Example: ContactsDemo.java

/**
* ContactsDemo.java
* Copyright (C) 2002-2003 Research In Motion Limited.
*/

package com.rim.docs.samples.pim;

import java.io.*;
import java.util.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.i18n.*;
import net.rim.device.api.system.*;
import net.rim.device.api.util.*;
import net.rim.blackberry.api.pim.*;
import com.rim.docs.samples.baseapp.*;

public final class ContactsDemo extends BaseApp


{
private ContactScreen _contactScreen;

public static void main(String[] args)


{
new ContactsDemo().enterEventDispatcher();
}
private ContactsDemo()
{
_contactScreen = new ContactScreen();
pushScreen(_contactScreen);
}
protected void onExit() { }

//inner class - creates Screen to add a contact


public final class ContactScreen extends MainScreen
{
private EditField _first, _last, _email, _phone, _pin;
private SaveMenuItem _saveMenuItem;

private class SaveMenuItem extends MenuItem


{
private SaveMenuItem()
{
super(null, 0, 100000, 5);

Developer Guide 51
Chapter 3: Integrating PIM functions

}
public String toString() {
return "Save";
}
public void run()
{
onSave();
}
}

public ContactScreen()
{
_saveMenuItem = new SaveMenuItem();
setTitle(new LabelField("Contacts Demo",
LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH));

_first = new EditField("First Name: ", "");


add(_first);

_last = new EditField("Last Name: ", "");


add(_last);

_email = new EditField("Email Address: ", "",


BasicEditField.DEFAULT_MAXCHARS, BasicEditField.FILTER_EMAIL);
add(_email);

_phone = new EditField("Work Phone: ", "",


BasicEditField.DEFAULT_MAXCHARS, BasicEditField.FILTER_PHONE);
add(_phone);

_pin = new EditField("PIN:", "", 8, BasicEditField.FILTER_HEXADECIMAL);


add(_pin);
}

protected boolean onSave()


{
String firstName = _first.getText();
String lastName = _last.getText();
String email = _email.getText();
String phone = _phone.getText();
String pin = _pin.getText();

//verify that a first or last name and email has been entered
if ((firstName.equals("") && lastName.equals("")) || email.equals(""))
{
Dialog.inform("You must enter a name and an email address!");
return false;
}

52 BlackBerry Java Development Environment


Address book

else
{
try
{
ContactList contactList = (ContactList)PIM.getInstance().
openPIMList(PIM.CONTACT_LIST, PIM.WRITE_ONLY);
Contact contact = contactList.createContact();

String[] name = new String[Contact.NAMESIZE];


//add values to PIM item
if (!firstName.equals(""))
{
name[Contact.NAME_GIVEN] = firstName;
}
if (!lastName.equals(""))
{
name[Contact.NAME_FAMILY] = lastName;
}
contact.addStringArray(Contact.NAME, Contact.ATTR_NONE, name);
contact.addString(Contact.EMAIL, Contact.ATTR_HOME, email);
contact.addString(Contact.TEL, Contact.ATTR_WORK, phone);
if (contactList.isSupportedField(BlackBerryContact.PIN)) {
contact.addString(BlackBerryContact.PIN, Contact.ATTR_NONE,
pin);
}
//save data to address book
contact.commit();

//reset UI fields
_first.setText("");
_last.setText("");
_email.setText("");
_phone.setText("");
_pin.setText("");
return true;
}
catch (PIMException e) {
return false;
}
}
}

protected void makeMenu(Menu menu, int instance) {


menu.add(_saveMenuItem);
super.makeMenu(menu, instance);
}
}
}

Developer Guide 53
Chapter 3: Integrating PIM functions

Tasks
Use an instance of the ToDoList class to store a list of tasks, or “to do” items. Create one or more
ToDo objects to store data for each task, such as summary, priority, due date, and status.

Open a task list


Invoke the PIM.openPIMList() method and specify the type of list to open (PIM.TODO_LIST) and the
access mode with which to open the list (READ_WRITE, READ_ONLY, or WRITE_ONLY).
ToDoList todoList = null;
try {
todoList = (ToDoList)PIM.getInstance().openPIMList(
PIM.TODO_LIST, PIM.READ_WRITE);
} catch (PimException e) {
//an error occurred
return;
}

Add a task
To create a ToDo item, invoke the createToDo() method on a task list.
ToDo task = todoList.createToDo();

Note: The task is not added to the database until you commit it. Refer to "Save a task" on page 55 for more
information.

Add task information


The ToDo class defines fields in which to store data, such as SUMMARY, PRIORITY, and DUE. Each field
has a specific data type, which you can retrieve by invoking PIMList.getFieldDataType(int
field). Depending on the data type of the field, set the field data by invoking one of the following
methods: addString(), addDate(), addInt(), addBoolean(), or addBinary().
Refer to "PIM API overview" on page 42 for more information on fields.
Before you attempt to set or retrieve a field, verify that the item supports the field by invoking
isSupportedField( int field ).

The following code demonstrates how to add fields to a task:


if (task.isSupportedField(ToDo.SUMMARY)) {
task.addString(ToDo.SUMMARY, ToDo.ATTR_NONE, "Create project plan");
}
if (task.isSupportedField(ToDo.DUE)) {
Date date = new Date();
task.addDate(ToDo.DUE, ToDo.ATTR_NONE, (date + 17280000));
}
54 BlackBerry Java Development Environment
Tasks

if (task.isSupportedField(ToDo.NOTE)) {
task.addString(ToDo.NOTE, ToDo.ATTR_NONE, "Required for meeting");
}
if (task.isSupportedField(ToDo.PRIORITY)) {
task.addInt(Todo.PRIORITY, ToDo.ATTR_NONE, 2);
}

To set a task status


To set a task status, use the PIM extended field ToDo.EXTENDED_FIELD_MIN_VALUE + 9. Values are
defined as follows:
STATUS_NOT_STARTED = 1
STATUS_IN_PROGRESS = 2
STATUS_COMPLETED = 3
STATUS_WAITING = 4
STATUS_DEFERRED = 5

For example, the following code sets a task status to “in progress”:
task.addInt(ToDo.EXTENDED_FIELD_MIN_VALUE + 9, ToDo.ATTR_NONE, 2);

Change task information


Invoke the appropriate set method, such as setString(), to replace an existing value with a new
value. Invoke countValues() to determine if a value is already set for the field.

Note: If you invoke an add method, such as addString(), when a value already exists, a FieldFullException
is thrown. Use the corresponding set method, such as setString(), to change an existing value.

For example, the following code changes the task summary:


if (task.countValues(ToDo.SUMMARY) > 0) {
task.setString(ToDo.SUMMARY, 0, ToDo.ATTR_NONE, "Review notes");
}

Save a task
Invoke the commit() method to save a task. You can invoke isModified() to determine whether any
of the item’s fields have changed since the item was last saved:
if(task.isModified()) {
task.commit();
}

Developer Guide 55
Chapter 3: Integrating PIM functions

Remove a task
To delete a ToDo item, invoke the removeToDo() method on a task list.
todoList.removeToDo(task);

Close a task list


Close the task list when you are finished with it.
try {
todoList.close();
} catch (PimException e) {
}

Retrieve task information


To retrieve task information, invoke PIMList.items() to enumerate through the list. For a particular
ToDo item, invoke PIMItem.getFields() to retrieve an array of IDs for fields that have data. Invoke
PIMItem.getString() to retrieve the field values.

The following example retrieves task information and print values for fields with String data:
ToDoList todoList = (ToDoList)PIM.getInstance().openToDoList(
PIM.TODO_LIST, PIM.READ_ONLY);
Enumeration e = todoList.items();
while (e.hasMoreElements()) {
ToDo task = (ToDo)e.nextElement();
int[] fieldIds = task.getFields();
int id;
for(int index = 0; index < fieldIds.length; ++index) {
id = fieldIds[index];
if(task.getPIMList().getFieldDataType(id) == STRING) {
for(int j=0; j < task.countValues(id); ++j) {
String value = task.getString(id, j);
System.out.println(task.getFieldLable(id) + "=" + value);
}
}
}

Convert to serial formats


You can import or export data in PIM items using standard formats, such as iCal and vCard. Invoke
PIM.supportedSerialFormats() and specify the list type (PIM.TODO_List) to retrieve a string array
of supported serial formats. Invoke toSerialFormat(PIMItem item, java.io.OutputStream os,
java.lang.String enc, java.lang.String dataFormat) to write an item to a serial format.

56 BlackBerry Java Development Environment


Tasks

Note: The enc parameter specifies the character encoding to use when writing to the output stream. Supported
character encodings include "UTF8," "ISO-8859-1," and "UTF-16BE." This parameter cannot be null.

The following example demonstrates how to export all the tasks from the handheld to a supported
serial format, using an output stream writer:
ToDoList todoList = (ToDoList)PIM.getInstance().openPIMList(
PIM.TODO_LIST, PIM.READ_ONLY);
ByteArrayOutputStream bs = new ByteArrayOutputStream();
String[] dataFormats = PIM.getInstance().supportedSerialFormats(
PIM.TODO_LIST);
Enumeration e = todoList.items();
while (e.hasMoreElements()) {
ToDo task = (ToDo)e.nextElement();
PIM.getInstance().toSerialFormat(task, bs, "UTF8", dataFormats[0]);
}

Import tasks
To import data into handheld tasks, invoke the fromSerialFormat(java.io.InputStream is,
java.lang.String enc ) method, which returns an array of PIMItem objects. Invoke the
ToDoList.importToDo() method add a new ToDo item.

Note: The enc parameter specifies the character encoding to use when writing to the output stream. Supported
character encodings include "UTF8," "ISO-8859-1," and "UTF-16BE." This parameter cannot be null.

The following example demonstrates how to convert an existing task (task) into a vCard and then
import the vCard into a new task (task2).
String[] dataFormats = PIM.toDoSerialFormats();

//write task to vCard


ByteArrayOutputStream os = new ByteArrayOutputStream();
PIM.getInstance().toSerialFormat(task, os, "UTF8", dataFormats[0]);

//import task from vCard


ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray))));
PIMItem[] pi = PIM.getInstance().fromSerialFormat(is, "UTF8");
ToDoList todoList = (ToDoList)PIM.getInstance().openPIMList(
PIM.TODO_LIST, PIM.READ_WRITE);
ToDo task2 = todoList.importToDo((ToDo)pi[0]);

Tip: The importToDo() method saves the task, so you do not have to invoke commit().

Developer Guide 57
Chapter 3: Integrating PIM functions

The following example displays a screen that enables the user to add a new task. After you save a
task, click the handheld Tasks icon to verify that the task was saved.

Example: TaskDemo.java

/**
* TaskDemo.java
* Copyright (C) 2002-2003 Research In Motion Limited.
*/

package com.rim.docs.samples.pim;

import java.io.*;
import java.util.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.i18n.*;
import net.rim.device.api.system.*;
import net.rim.device.api.util.*;
import net.rim.blackberry.api.pim.*;
import com.rim.docs.samples.baseapp.*;

public final class TaskDemo extends BaseApp


{
private TaskScreen _taskScreen;

public static void main(String[] args)


{
new TaskDemo().enterEventDispatcher();
}

private TaskDemo()
{
_taskScreen = new TaskScreen();
pushScreen(_taskScreen);
}

protected void onExit() { }

public final class TaskScreen extends MainScreen


{
//members
private EditField _summary, _note;

58 BlackBerry Java Development Environment


Tasks

private DateField _due;


private ObjectChoiceField _priority, _status;

private SaveMenuItem _saveMenuItem;

private class SaveMenuItem extends MenuItem


{
private SaveMenuItem()
{
super(null, 0, 100000, 5);
}

public String toString() {


return "Save";
}

public void run()


{
onSave();
}
}

public TaskScreen()
{
_saveMenuItem = new SaveMenuItem();

setTitle(new LabelField("Tasks Demo",


LabelField.ELLIPSIS | LabelField.USE_ALL_WIDTH));

_summary = new EditField("Task Summary: ", "");


add(_summary);

//in TODO.Priority, 0 to 9 is highest to lowest priority


String[] choices = {"High", "Normal", "Low"};
_priority = new ObjectChoiceField("Priority: ", choices, 1);
add(_priority);

String[] status = { "Not Started", "In Progress", "Completed",


"Waiting on someone else", "Deferred" };
_status = new ObjectChoiceField("Status: ", status, 0);
add(_status);

_due = new DateField("Due: ", System.currentTimeMillis() + 3600000,


DateField.DATE_TIME);
add(_due);

_note = new EditField("Extra Notes: ", "");


add(_note);

Developer Guide 59
Chapter 3: Integrating PIM functions

}
protected boolean onSave()
{
try
{
ToDoList todoList = (ToDoList)PIM.getInstance().
openPIMList(PIM.TODO_LIST, PIM.WRITE_ONLY);
ToDo task = todoList.createToDo();
task.addDate(ToDo.DUE, ToDo.ATTR_NONE, _due.getDate());
task.addString(ToDo.SUMMARY, ToDo.ATTR_NONE, _summary.getText());
task.addString(ToDo.NOTE, ToDo.ATTR_NONE, _note.getText());
task.addInt(ToDo.PRIORITY, ToDo.ATTR_NONE,
_priority.getSelectedIndex());

//ToDo.EXTENDED_FIELD_MIN_VALUE + 9 represents status


//add 1 to selected index so that values are correct
//refer to RIM Implementation Notes in API docmentation for ToDo
task.addInt(ToDo.EXTENDED_FIELD_MIN_VALUE + 9, ToDo.ATTR_NONE,
_status.getSelectedIndex() + 1);

//save task to handheld tasks


task.commit();

_summary.setText("");
_note.setText("");
_due.setDate(null);
_priority.setSelectedIndex(1); //reset to “Normal” priority
_status.setSelectedIndex(0); //reset to “Not Started” status

return true;
}
catch (PIMException e)
{
return false;
}
}

protected void makeMenu(Menu menu, int instance)


{
menu.add(_saveMenuItem);
super.makeMenu(menu, instance);
}
}
}

60 BlackBerry Java Development Environment


Calendar

Calendar
Use an instance of the EventList class to access the handheld calendar. Create one or more Event
objects to store information for specific appointments. For each event, you can store data such as the
summary, location, start and end time, and reminder notification.

Open a list
You must create an EventList before you can add events. Invoke the PIM.openPIMLIst() method
and specify the type of list to open (PIM.EVENT_LIST) and the mode in which to open the list
(READ_WRITE, READ_ONLY, or WRITE_ONLY).
EventList eventList = null;
try {
eventList = (EventList)PIM.getInstance().openPIMList(
PIM.EVENT_LIST, PIM.READ_WRITE);
} catch (PimException e) {
}

Create an appointment
To create an Event object, invoke the createEvent() method on an event list.
Event event = eventList.createEvent();

Note: The appointment is not added to the database until you commit it. Refer to "Save an appointment" on
page 63 for more information.

Add appointment information


The Event class defines fields in which to store data, such as SUMMARY, LOCATION, START, END, and
ALARM. Each field has a specific data type, which you can retrieve by invoking
PIMList.getFieldDataType(int field). Depending on the data type of the field, set the field data
by invoking one of the following methods: addString(), addDate(), addInt(), addBoolean(), or
addBinary().

Before you attempt to set or retrieve a field, verify that the item supports the field by invoking
isSupportedField( int field ).

Developer Guide 61
Chapter 3: Integrating PIM functions

The following sample code demonstrates how to add appointment information.


if (event.isSupportedField(Event.SUMMARY)) {
event.addString(Event.SUMMARY, Event.ATTR_NONE, "Meet with customer");
}
if (event.isSupportedField(Event.LOCATION)) {
event.addString(Event.LOCATION, Event.ATTR_NONE, "Conference Center");
}

Date start = new Date(System.currentTimeMillis() + 8640000);


if (event.isSupportedField(Event.START)) {
event.addDate(Event.START, Event.ATTR_NONE, start);
}
if (event.isSupportedField(Event.END)) {
event.addDate(Event.END, Event.ATTR_NONE, start + 72000000);
}
if (event.isSupportedField(Event.ALARM)) {
event.addDate(Event.ALARM, Event.ATTR_NONE, start - 396000);
}

Tip: If the Event.ALARM field is not set, by default an appointment reminder is set to 15 minutes before the start
of the event.

Change appointment information


Invoke the appropriate set method, such as setString(), to replace an existing value with a new
value. Invoke countValues() to determine if a value is already set for the field.
For example, the following code changes the appointment location:
if (event.countValues(Event.LOCATION) > 0) {
event.setString(Event.LOCATION, 0, Event.ATTR_NONE, "Board Room");
}

Note: If you invoke an add method, such as addString(), when a value already exists, a FieldFullException
is thrown. Use the corresponding set method, such as setString(), to change an existing value.

Create a recurring appointment


Use a RepeatRule object to define a recurrence pattern for an appointment that recurs at regular
intervals. The RepeatRule class defines fields for the properties and values that you can set, such as
COUNT, FREQUENCY, and INTERVAL. Invoke RepeatRule.getFields() to retrieve an array of supported
fields.
1. Create a RepeatRule instance.

62 BlackBerry Java Development Environment


Calendar

RepeatRule recurring = new RepeatRule();

2. Invoke the setInt or setDate methods on the RepeatRule object to define when the
appointment recurs.

recurring.setInt(RepeatRule.FREQUENCY, RepeatRule.MONTHLY);
recurring.setInt(RepeatRule.DAY_IN_MONTH, 14);

3. Invoke the setRepeat() method on an event to set its recurrence pattern.

EventList eventList = (EventList)PIM.getInstance().openPIMList(


PIM.EVENT_LIST, PIM.READ_WRITE);
Event event = eventList.createEvent();
event.setRepeat(recurring);

Save an appointment
Invoke the commit() method to save an appointment. You can invoke isModified() to determine
whether any of the item’s fields have changed since the item was last saved:
if(event.isModified()) {
event.commit();
}

Close the event list


Close the event list when you finish using it.
try {
eventList.close();
} catch (PimException e) {
//
}

Retrieve appointment information


To retrieve appointment information, invoke PIMList.items() to enumerate through the list. For a
particular ToDo item, invoke PIMItem.getFields() to retrieve an array of IDs for fields that have
data. Invoke PIMItem.getString() to retrieve the field values.
The following example demonstrates to how to retrieve appointment information and print values
for fields with String data:
EventList eventList = (EventList)PIM.getInstance().openPIMList(
PIM.EVENT_LIST, PIM.READ_ONLY);

Enumeration e = eventList.items();
while (e.hasMoreElements()) {

Developer Guide 63
Chapter 3: Integrating PIM functions

Event event = (Event)e.nextElement();


int[] fieldIds = event.getFields();
int id;
for(int index = 0; index < fieldIds.length; ++index) {
id = fieldIds[index];
if(e.getPIMList().getFieldDataType(id) == STRING) {
for(int j=0; j < event.countValues(id); ++j) {
String value = event.getString(id, j);
System.out.println(event.getFieldLable(id) + "=" + value);
}
}
}

Convert to serial formats


You can import or export data in PIM items using standard formats, such as iCal and vCard. Invoke
PIM.supportedSerialFormats() and specify the list type (PIM.CONTACT_LIST) to retrieve a string
array of supported serial formats. Invoke toSerialFormat(PIMItem item, java.io.OutputStream
os, java.lang.String enc, java.lang.String dataFormat) to write an item to a serial format.

Note: The enc parameter specifies the character encoding to use when writing to the output stream. Supported
character encodings include "UTF8," "ISO-8859-1," and "UTF-16BE". This parameter cannot be null.

The following example demonstrates how to export all the tasks from the handheld to a supported
serial format, using an output stream writer:
EventList eventList = (EventList)PIM.getInstance().openPIMList(
PIM.EVENT_LIST, PIM.READ_ONLY);

ByteArrayOutputStream bs = new ByteArrayOutputStream();


String[] dataFormats = PIM.getInstance().supportedSerialFormats(
PIM.CONTACT_LIST);

Enumeration e = eventList.items();
while (e.hasMoreElements()) {
Event event = (Event)e.nextElement();
PIM.getInstance().toSerialFormat(event, bs, "UTF8", dataFormats[0]);
}

Import appointments
To import data into the handheld calendar, invoke the fromSerialFormat(java.io.InputStream
is, java.lang.String enc ) method, which returns an array of PIMItem objects. Invoke the
EventList.importEvent() method to add a new appointment.

Note: The enc parameter specifies the character encoding to use when writing to the output stream. Supported
character encodings include "UTF8," "ISO-8859-1," and "UTF-16BE." This parameter cannot be null.

64 BlackBerry Java Development Environment


Calendar

The following example demonstrates how to convert an existing appointment into a vCard and then
import the vCard as a new appointment.
String[] dataFormats = PIM.eventSerialFormats();

//write appointment to vCard


ByteArrayOutputStream os = new ByteArrayOutputStream();
PIM.getInstance().toSerialFormat(event, os, "UTF8", dataFormats[0]);
//import appointment from vCard
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray))));
PIMItem[] pi = PIM.getInstance().fromSerialFormat(is, "UTF8");
EventList eventList = (EventList)PIM.getInstance().openPIMList(
PIM.EVENT_LIST, PIM.READ_WRITE);

Event event2 = eventList.importEvent((Event)pi[0]);

Tip: The importEvent() method saves the appointment, so you do not have to invoke commit().

The following example displays a screen that enables a user to create a new, recurring appointment
in the handheld calendar. You could combine this sample with the ContactsDemo.java sample to
allow the user the invite attendees to the meeting (refer to page 51). After you save an appointment,
click the Calendar icon to verify that the appointment was saved.

Example: EventDemo.java

/**
* EventDemo.java
* Copyright (C) 2002-2003 Research In Motion Limited.
*/

package com.rim.docs.samples.pim;

import java.io.*;
import java.util.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.i18n.*;
import net.rim.device.api.system.*;
import net.rim.device.api.util.*;
import net.rim.blackberry.api.pim.*;
import com.rim.docs.samples.baseapp.*;

Developer Guide 65
Chapter 3: Integrating PIM functions

public final class EventDemo extends BaseApp


{
private EventScreen _eventScreen;
public static void main(String[] args)
{
new EventDemo().enterEventDispatcher();
}
private EventDemo()
{
_eventScreen = new EventScreen();
pushScreen(_eventScreen);
}

protected void onExit() { }

public final class EventScreen extends MainScreen


{
private EditField _subject, _location;
private SaveMenuItem _saveMenuItem;
private DateField _startTime, _endTime;
private ObjectChoiceField _repeat;
private Event event;

private class SaveMenuItem extends MenuItem


{
public SaveMenuItem()
{
super(null, 0, 100000, 5);
}
public String toString() {
return "Save";
}
public void run()
{
onSave();
}
}

public EventScreen()
{
_saveMenuItem = new SaveMenuItem();
setTitle(new LabelField("Event Demo", LabelField.ELLIPSIS |
LabelField.USE_ALL_WIDTH) );

_subject = new EditField("Subject: ", "");


add(_subject);

_location = new EditField("Location: ", "");

66 BlackBerry Java Development Environment


Calendar

add(_location);

_startTime = new DateField("Start: ", System.currentTimeMillis() +


3600000, DateField.DATE_TIME);
_endTime = new DateField("End: ", System.currentTimeMillis() +
7200000, DateField.DATE_TIME);

add(new SeparatorField());

add(_startTime);
add(_endTime);

add(new SeparatorField());

String[] choices = {"None", "Daily", "Weekly", "Monthly", "Yearly"};


_repeat = new ObjectChoiceField("Recurrence: ", choices, 0);
add(_repeat);
}

protected boolean onSave()


{
try
{
EventList eventList = (EventList)PIM.getInstance().
openPIMList(PIM.EVENT_LIST, PIM.WRITE_ONLY);
event = eventList.createEvent();
event.addString(Event.SUMMARY, PIMItem.ATTR_NONE,
_subject.getText());
event.addString(Event.LOCATION, PIMItem.ATTR_NONE,
_location.getText());

event.addDate(Event.END, PIMItem.ATTR_NONE, _endTime.getDate());


event.addDate(Event.START, PIMItem.ATTR_NONE,
_startTime.getDate());

if(_repeat.getSelectedIndex() != 0) {
event.setRepeat(setRule());
}

//save appointment to Calendar


event.commit();

//reset fields on screen


_subject.setText("");
_location.setText("");
_endTime.setDate(null);
_startTime.setDate(null);

Developer Guide 67
Chapter 3: Integrating PIM functions

_repeat.setSelectedIndex(0);

return true;
}
catch (PIMException e)
{
System.err.println(e);
}
return false;
}
private RepeatRule setRule() {

RepeatRule rule = new RepeatRule();


int index = _repeat.getSelectedIndex();
if (index == 0) {
rule.setInt(RepeatRule.FREQUENCY, RepeatRule.DAILY);
}
if (index == 1) {
rule.setInt(RepeatRule.FREQUENCY, RepeatRule.WEEKLY);
}
if (index == 2) {
rule.setInt(RepeatRule.FREQUENCY, RepeatRule.MONTHLY);
}
if (index == 3) {
rule.setInt(RepeatRule.FREQUENCY, RepeatRule.YEARLY);
}
return rule;
}
protected void makeMenu(Menu menu, int instance)
{
menu.add(_saveMenuItem);
/*int i =*/ menu.addSeparator();
super.makeMenu(menu, instance);
}
}
}

68 BlackBerry Java Development Environment


Deploying your application

Deploying your application


The BlackBerry PIM APIs are not installed on the handheld by default, but the .cod files for the APIs
are included in the BlackBerry Desktop Software. Your application can load the APIs when it is
loaded.
1. In the IDE, select the project for your application.
2. On the Project menu, click Generate .alx File. An .alx file for the project is created in to folder in
which the .jdp project file is located.
3. Edit the .alx file to specify that your application is dependent on the BlackBerry PIM APIs.
Add the <requires id="net.rim.blackberry.api.mail"> tag inside the <application> tag:

<application id="sampleapp">
<requires id="net.rim.blackberry.api" />
<name>Sample application</name>
...

Refer to "Format of .alx files" on page 173 for more information.


When a user loads your application, the BlackBerry PIM APIs are also loaded if they are not
already on the user’s handheld.

Note: The .cod files for the BlackBerry PIM API that are included with the desktop software can change between
release of the desktop software and release of the BlackBerry JDE. In this case, you must redistribute updated API
.cod files with your application. Installation packages will be provided on the BlackBerry Developer Zone web
site. Refer to the BlackBerry JDE release notes for more information.

Developer Guide 69
Chapter 3: Integrating PIM functions

70 BlackBerry Java Development Environment


Chapter 4
Adding options
This section provides information on the following topics:
• Options API
• Registering to add options
• Adding an option item
• Storing options
Chapter 4: Adding options

Options API
The BlackBerry Options API, in the net.rim.blackberry.api.options package, enables you to add
items to the handheld Options screen. Use this capability to add system-wide options to the
handheld that multiple applications can use.
When the user clicks the Options icon on the handheld Home screen, a list of options appears, such
as AutoText, Date/Time, and Firewall. The user can select one of these items to view a screen for that
particular option. The screen displays one or more fields. Typically, the user can change the value of
each field.
For example, on the Date/Time option screen, there are fields such as Time Zone, Time,
Time Format, and so on. The user can change the value of any of these fields.
To add an option item, complete the following tasks:
• register with the handheld to provide options
• add an item to the Options screen
• create a persistent object in which to store option data
These steps are explained in the following sections.

Registering to add options


Your application must implement the OptionsProvider interface and register as an options provider
to add option items.

Create a library project


To add option items when the handheld starts, create a library project with a libMain() method to
perform the required registration.
1. In the IDE, create a project.
2. Right-click the project and click Properties. The Properties window appears.
3. Click the Application tab.
4. From the Project type drop-down list, select Library.
5. Select the Auto-run on startup option.
6. Click OK.

72 BlackBerry Java Development Environment


Adding an option item

Implement OptionsProvider
Your application must implement the OptionsProvider interface and register as an options provider
to add option items.

public final class DemoOptionsProvider implements OptionsProvider {

Register as an options provider


Write a method to retrieve a static instance of your class. Only one instance should exist at a time.
private static DemoOptionsProvider _instance;
...
public static DemoOptionsProvider getInstance() {
if(_instance == null) {
_instance = new DemoOptionsProvider("Options Demo");
}
return _instance;
}

In the libMain() method of your library class, invoke


OptionsManager.registerOptionsProvider(). Invoke the getInstance() method that you defined
earlier to retrieve an instance of your DemoOptionsProvider class.
public static void libMain(String[] args) {
OptionsManager.registerOptionsProvider(getInstance());
}

Tip: In a library project, the libMain() method is invoked when the handheld starts, if you selected the
Auto-run on startup option for the library project. In the IDE, right-click the project, click Properties, and click
the Application tab.

Adding an option item


Perform the following steps to add a new item to the handheld Options screen. Your application
must implement three methods on the OptionsProvider interface: getTitle(), save(), and
populateMainScreen().

1. Add variables to represent each field on the screen for the new option item. In this example, the
option item has one field (_ocf ). Create a variable for the persistent object that stores the
selected option value (_data). Refer to "Storing options" on page 75 for more information.

private ObjectChoiceField _ocf;


private OptionsDemoData _data;

Developer Guide 73
Chapter 4: Adding options

2. Implement the populateMainScreen() method to add one or more fields to the screen for your
options item.
In this example, an ObjectChoiceField is used to display options to the user. From the
persistent object that stores your options data, retrieve the initial value to display in this field .

protected void populateMainScreen(MainScreen screen)


{
int index = _data.getSelected(); //current value
String[] choices = {"High", "Low", "None"};
_ocf = new ObjectChoiceField("Security: ", choices, index);
screen.add(_ocf);
}

3. Implement the save() method to save changes to any fields on the screen.

protected boolean save()


{
_data.setSelected(_ocf.getSelectedIndex());
_data.commit();
}

To retrieve the index of the currently selected value, invoke the getSelectedIndex() method on
the ObjectChoiceField (_ocf). To save the selected values for each field, invoke
setSelected() and commit() on the persistent object.

4. Implement the getTitle() method to retrieve the title for the options item.

public String getTitle() {


return _title;
}

5. Define a constructor for your application that accepts as a parameter the title of the options item
to display in the Options application. In the constructor, set the title to the value that is provided
when the constructor is called, and invoke the load() method to load the persistent object that
stores the current option value.

private String _title;


private DemoOptionsProvider(String title) {
_title = title;
_data = OptionsDemoData.load();
}

74 BlackBerry Java Development Environment


Storing options

Storing options
To store the option value that is currently selected, create an inner class that implements the
Persistable interface. In this class, define methods for setting the selected option value, and
committing and retrieving an option value in the persistent store.

Note: Make this inner class—and its get, set, and commit methods—public so that other applications can
access your options data.

Refer to "Storing persistent data" on page 79 for more information on storing persistent data.
//inner class in which to store selected option values
public static final class OptionsDemoData implements Persistable
{
private static final long ID = 0x6af0b5eb44dc5164L;
private int _selectedOption;

private OptionsDemoData()
{
}

public int getSelected()


{
return _selectedOption;
}
public void setSelected(int index)
{
_selectedOption = index;
}

public void commit()


{
PersistentObject.commit(this);
}

private static OptionsDemoData load()


{
PersistentObject persist = PersistentStore.getPersistentObject(
OptionsDemoData.ID );
synchronized( persist ) {
if( persist.getContents() == null ) {
persist.setContents( new OptionsDemoData() );
persist.commit();
}
}
return (OptionsDemoData)persist.getContents();
}
}

Developer Guide 75
Chapter 4: Adding options

Providing access to option data


In your library class, add a public method to enable other applications to access your option data.
For example, add a public getData() method that returns the persistent object that stores your
options data.
public OptionsDemoData getData() {
return _data;
}

Example: DemoOptionsProvider.java
/**
* DemoOptionsProvider.java
* Copyright 2002-2003 Research In Motion Limited.
*/

package com.rim.docs.samples.options;

import net.rim.blackberry.api.options.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.i18n.*;
import net.rim.device.api.system.*;
import net.rim.device.api.util.*;

//a simple library class to demonstrate the use of the options facilities
public final class DemoOptionsProvider implements OptionsProvider
{
//members
private ObjectChoiceField _ocf;
private OptionsDemoData _data;
private String _title;
private static DemoOptionsProvider _instance;

//constructors
private DemoOptionsProvider()
{
}
private DemoOptionsProvider(String title)
{
_title = title;
_data = OptionsDemoData.load();
}

76 BlackBerry Java Development Environment


Providing access to option data

//only allow one instance of this class


public static DemoOptionsProvider getInstance() {
if (_instance == null) {
_instance = new DemoOptionsProvider("Options Demo");
}
return _instance;
}
//on startup, create the instance and register it
public static void libMain(String[] args)
{
OptionsManager.registerOptionsProvider(getInstance());
}
//get the title for the option item
public String getTitle() {
return _title;
}

//add fields to the screen


public void populateMainScreen(MainScreen screen) {
int index = _data.getSelected();
String[] choices = {"High", "Low", "None"};
_ocf = new ObjectChoiceField("Security: ", choices, index);
screen.add(_ocf);
}

//save the data


public void save()
{
_data.setSelected(_ocf.getSelectedIndex());
_data.commit();
}

//retrieve the data - used by other applications to access options data


public OptionsDemoData getData() {
return _data;
}

//inner class to store selected option values


public static final class OptionsDemoData implements Persistable
{
private static final long ID = 0x6af0b5eb44dc5164L;
private int _selectedOption;

private OptionsDemoData()
{
}

Developer Guide 77
Chapter 4: Adding options

public int getSelected()


{
return _selectedOption;
}

public void setSelected(int index)


{
_selectedOption = index;
}

public void commit()


{
PersistentObject.commit(this);
}

private static OptionsDemoData load()


{
PersistentObject persist = PersistentStore.getPersistentObject(
OptionsDemoData.ID );
synchronized( persist ) {
if( persist.getContents() == null ) {
persist.setContents( new OptionsDemoData() );
persist.commit();
}
}
return (OptionsDemoData)persist.getContents();
}
}
}

78 BlackBerry Java Development Environment


Chapter 5
Storing persistent data
This section provides information on the following topics:
• Choosing how to store data
• Working with persistent data
• Working with custom objects
Chapter 5: Storing persistent data

Choosing how to store data


You can store data on the handheld in one of two ways:
• using MIDP record stores
• using the BlackBerry persistence model
You must use the MIDP implementation if you want your application to be portable across multiple
devices that are compatible with the Java™ 2 Platform, Micro Edition (J2ME™). If you are writing
an application specifically for the BlackBerry handheld, you typically use the BlackBerry persistence
model because it provides a more flexible and efficient way to store data.

Using RecordStore
The MIDP record store implementation is provided in the javax.microedition.rms package.
Persistent data is stored in RecordStore objects. A record store can be a maximum of 64 KB.
To create a record store, invoke the openRecordStore method:
RecordStore store = RecordStore.openRecordStore("Contacts", true);

The true parameter indicates that the record store should be created if it does not exist. Each record
store has a unique name within a MIDlet suite. A MIDlet can only access record stores that are
created by a MIDlet in the same suite.
Discrete units of data are called records. A record is an array of bytes that is assigned a unique
identification number. The following code demonstrates how to add a record to the record store:
int id = store.addRecord(_data.getBytes(), 0, data.length());
store.closeRecordStore();

To retrieve the record, invoke getRecord and specify the record ID:
byte[] data = new byte[store.getRecordSize(id)];
store.getRecord(id, data, 0);
String dataString = new String(data);

The following code demonstrates how to retrieve all records in a record store:
RecordStore store = RecordStore.openRecordStore("Contacts", false);
RecordEnumeration e = store.enumerateRecords(null, null, false);
store.closeRecordStore();

80 BlackBerry Java Development Environment


Choosing how to store data

The enumerateRecords method accepts three parameters:


• filter: specifies a RecordFilter object to retrieve a subset of record store records (if null, all
record store records are returned)
• comparator: specifies a RecordComparator object to determine the order in which records are
returned (if null, records are returned in an undefined order)
• keepUpdated: determines whether or not the enumeration is kept current with changes to the
record store

Using BlackBerry persistent storage


The BlackBerry API provides a more efficient way to store data on the handheld than the record
store model provided by MIDP.
There are two main differences between the MIDP RecordStore and the BlackBerry persistence
model (PersistentStore):
• Data storage: MIDP records store data only as byte arrays. In contrast, the BlackBerry APIs
enable you to save any object in the persistent store. As a result, searching for stored data is
much faster than in the record model. To store custom object types, the class must implement
the Persistable interface.
• Data sharing: In MIDP, each RecordStore belongs to a single MIDlet suite and a MIDlet can
only access record stores that are created by a MIDlet in the same suite. In the BlackBerry
persistence model, however, data can be shared between applications, at the discretion of the
application that creates the data. With the use of code signing, only authorized applications can
access the data.

Note: The BlackBerry persistence API is available only with BlackBerry handheld software version 3.6 or later. On
earlier versions of handheld software, you must use the MIDP record store.

Developer Guide 81
Chapter 5: Storing persistent data

Conserving storage space


The BlackBerry Wireless Handheld has a finite amount of storage space. You should design your
application carefully to minimize the amount of memory that is required to store persistent data.
On a typical BlackBerry handheld, storage space is required for standard BlackBerry applications.
The remaining storage space must be shared between all applications to store user data, including
calendar appointments, address book items, and email messages.
If the handheld is operating with a low amount of memory, it might perform the following actions
to increase memory space:
• delete old email messages from the handheld
• delete calendar appointments that are more than one-week old from the handheld (if wireless
calendar synchronization is enabled)
Data is not deleted from the user’s desktop email program.

Tip: Users can view the current amount of available data storage in the handheld Options application, on the
Status screen.

Backup and restore


The synchronization API, in the net.rim.device.api.synchronization package, enables you to
back up and restore custom persistent data on the handheld. Refer to "Backing up and restoring
persistent data" on page 97 for more information.

Security
By default, applications on the handheld that have been digitally signed by RIM can access your
data in the persistent store. For information on how to control access to your data, contact RIM.

Administrative control
With the BlackBerry Enterprise Server version 3.5 Service Pack 2 or later for Microsoft Exchange or
BlackBerry Enterprise Server version 2.2 or later for Lotus Domino, system administrators can use
IT policies to control the use of persistent storage by third-party applications.
Administrators can set the IT policy item ALLOW_USE_PERSISTENT_STORE to TRUE or FALSE. The
default setting is TRUE: third-party applications are allowed to use persistent storage.

Note: This policy item does not affect the use of the MIDP record store.

82 BlackBerry Java Development Environment


Working with persistent data

Data integrity
To maintain the integrity of data in persistent storage, partial updates are not made if an error
occurs during a commit.
It is possible for data integrity to be compromised when the VM performs an emergency garbage
collection due to low memory. In this case, outstanding transactions are committed immediately. If
the handheld fails during this operation, the partially completed transactions are committed when
the handheld starts. Outstanding transactions are not committed during normal garbage collection.

Working with persistent data


This section explains how to create an application that enables users to type a user name and
password and to save this data in the persistent store. To create this application, complete the
following tasks:
• create a database
• create a Save menu item to save data
• create a Get menu item to retrieve data
The following example demonstrates how to create an application for users to view their current
user name and password, type a new user name and password, and save changes.

UserInfo.java – main screen


The complete code sample is provided at the end of this section. Refer to "UserInfo.java" on page 86
for more information.

Developer Guide 83
Chapter 5: Storing persistent data

Create a database
The following sample code demonstrates how to create a persistent database:
static PersistentObject store;
static {
store = PersistentStore.getPersistentObject( 0xa1a569278238dad2L );
}

Tip: Use a static constructor so that only one PersistentObject is created, the first time that an object of this
class is created. Each time a process starts, the static blocks that it contains are run again.

Each application typically creates a single PersistentObject, which it uses as its root database for
persistent data and indexes. The application can then save data into this PersistentObject. All
native data types can be stored persistently. Custom data types can be stored persistently if the class
implements the Persistable interface.
Each PersistentObject is identified by a unique long key. You should typically use a hash of your
fully qualified package name.
1. In the IDE, type a string value, such as com.rim.docs.samples.userinfo.
2. Select this string.
3. Right-click and click Convert 'com.rim.docs.samples.userinfo' to long. The long value appears.
Include a comment in your code to indicate the string that was used to generate the long key.

Save data
To save data to the persistent store, invoke setContents() on a PersistentObject. This method
replaces existing content with the new content. Invoke commit() to save to the persistent store.

Note: If an error occurs during a commit, partial updates are not committed. Data in the PersistentObject
retains the values from the last commit. Data integrity is preserved.

The following code sample creates a Save menu item, which saves a user name and password that
the user typed as a string array in the PersistentObject that you created in the previous section.
private MenuItem saveItem = new MenuItem(_resources, MENUITEM_SAVE, 110, 10) {
public void run() {
String username = new String(usernamefield.getText());
String password = new String(passwordfield.getText());
String[] userinfo = {username, password};
synchronized(store) {
store.setContents(userinfo);
store.commit();
}

84 BlackBerry Java Development Environment


Working with persistent data

Dialog.inform(_resources.getString(APP_SUCCESS));
usernamefield.setText(null);
passwordfield.setText(null);
}
};

Retrieve data
To retrieve data from the persistent store, invoke getContents() on a PersistentObject.
The following code sample demonstrates how to retrieve the user name and password from the
datastore and display this information to the user. You must perform an explicit cast on the object
that is returned by PersistentObject.getContents() to convert it to a String array.
private MenuItem getItem = new MenuItem(_resources, MENUITEM_GET, 110, 11) {
public void run() {
synchronized(store) {
if(store.getContents() == null) {
Dialog.alert(_resources.getString(APP_ERROR));
} else {
String[] currentinfo = (String[])store.getContents();
currentusernamefield.setText(currentinfo[0]);
currentpasswordfield.setText(currentinfo[1]);
}
}
}
};

Tip: When an application first accesses a database, it should verify the order of any indexes and recreate the
index if a problem exists. Applications should also be able to determine and correct any problems with corrupt
or missing data. Refer to "Data integrity" on page 83 for more information.

Delete a database
To delete a database, invoke PersistentStore.destroyPersistentObject(). For example, to
remove the PersistentObject that is used in the previous examples, write the following code:
PersistentStore.destroyPersistentObject(0xa1a569278238dad2L);

You must provide the unique key for the PersistentObject.

Note: The PersistentObject is used as the root database for the application. By deleting it, you permanently
remove all persistent data that the application has stored.

To delete individual data, simply treat them as normal objects and remove references to them. They
are garbage collected automatically.

Developer Guide 85
Chapter 5: Storing persistent data

Example: UserInfo.java
/**
* UserInfo.java
* Copyright (C) 2001-2003 Research In Motion Limited. All rights reserved.
*/

package com.rim.docs.samples.userinfo;

import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.system.*;
import net.rim.device.api.util.*;
import java.util.*;
import net.rim.device.api.i18n.*;
import com.rim.docs.samples.baseapp.*;

public class UserInfo extends BaseApp implements UserInfoResource,


KeyListener, TrackwheelListener {

private static PersistentObject store;


private static ResourceBundle _resources;
private AutoTextEditField usernamefield;
private PasswordEditField passwordfield;
private AutoTextEditField currentusernamefield;
private AutoTextEditField currentpasswordfield;

static {
_resources = ResourceBundle.getBundle(
UserInfoResource.BUNDLE_ID, UserInfoResource.BUNDLE_NAME);
store = PersistentStore.getPersistentObject(0xa1a569278238dad2L);
}

private MenuItem saveItem = new MenuItem(_resources, MENUITEM_SAVE, 110, 10) {


public void run() {
String username = new String(usernamefield.getText());
String password = new String(passwordfield.getText());
String[] userinfo = {username, password};
synchronized(store) {
store.setContents(userinfo);
store.commit();
}

Dialog.inform(_resources.getString(APP_SUCCESS));

86 BlackBerry Java Development Environment


Working with persistent data

usernamefield.setText(null);
passwordfield.setText(null);
}
};

private MenuItem getItem = new MenuItem(_resources, MENUITEM_GET, 110, 11) {


public void run() {
synchronized(store) {
if(store.getContents() == null) {
Dialog.alert(_resources.getString(APP_ERROR));
}
else {
String[] currentinfo = (String[])store.getContents();
currentusernamefield.setText(currentinfo[0]);
currentpasswordfield.setText(currentinfo[1]);
}
}
}
};

public static void main(String[] args) {


UserInfo app = new UserInfo();
app.enterEventDispatcher();
}

public UserInfo()
{
MainScreen mainScreen = new MainScreen();
mainScreen.setTitle(new LabelField(
_resources.getString(APPLICATION_TITLE)));

usernamefield = new AutoTextEditField(


_resources.getString(FIELD_NAME), "");
passwordfield = new PasswordEditField(
_resources.getString(FIELD_PASSWORD), "");
currentusernamefield = new AutoTextEditField(
_resources.getString(FIELD_CURRENTNAME), "");
currentpasswordfield = new AutoTextEditField(
_resources.getString(FIELD_CURRENTPASSWORD), "");

SeparatorField separator = new SeparatorField();

mainScreen.add(usernamefield);
mainScreen.add(passwordfield);
mainScreen.add(separator);
mainScreen.add(currentusernamefield);
mainScreen.add(currentpasswordfield);

Developer Guide 87
Chapter 5: Storing persistent data

mainScreen.addKeyListener(this);
mainScreen.addTrackwheelListener(this);
pushScreen(mainScreen);
}
public void makeMenu( Menu menu, int instance) {
menu.add(saveItem);
menu.add(getItem);
super.makeMenu(menu, 0);
}
public void onExit()
{
Dialog.alert(_resources.getString(APP_EXIT));
}
}

Working with custom objects


This section explains how to create an application for users to store information about their favorite
restaurant.

Restaurants.java – main screen


To create this application, complete the following steps:
• create a database
• create an object type that can be saved persistently
• create a Save menu item to save the object
• create a Get menu item to retrieve the object

88 BlackBerry Java Development Environment


Working with custom objects

The complete code sample is provided at the end of this section. Refer to "Restaurants.java" on page
92 for more information.
In this example, users can save information about multiple restaurants, but they can only view
information about the most recently saved restaurant. At the end of this guide, another sample
application is provided that enables users to view and edit several restaurants. Refer to "Persistence
sample application" on page 158 for more information.

Create a database
Create a Vector object to store multiple objects.
As in the previous example, create a single PersistentObject as the root database for your
application. Initialize this database to store a Vector.
private static Vector _data;

PersistentObject store;
static {
store = PersistentStore.getPersistentObject( 0xdec6a67096f833cL );
//key is hash of test.samples.restaurants
synchronized (store) {
if (store.getContents() == null) {
store.setContents(new Vector());
store.commit();
}
}
_data = new Vector();
_data = (Vector)store.getContents();
}

Create an object that can saved


The following code sample creates an inner class that defines a new type of object to store
information about a restaurant. This class defines a Vector with four elements to store the restaurant
name, address, phone number, and specialty, as well as methods to retrieve and set values for
Vector elements.

For an object to be stored persistently, its class must implement the Persistable interface. There are
no methods in the Persistable interface.

Note: A class must explicitly implement Persistable for objects of the class to be saved persistently.
This requirement applies even to subclasses. For example, if class A implements Persistable, and it has a
subclass B, objects of subclass B cannot be stored persistently unless class B also implements Persistable.

Developer Guide 89
Chapter 5: Storing persistent data

private static final class RestaurantInfo implements Persistable {


private Vector _elements;
public static final int NAME = 0;
public static final int ADDRESS = 1;
public static final int PHONE = 2;
public static final int SPECIALTY = 3;

public RestaurantInfo()
{
_elements = new Vector(4); //initial capacity of 4
for ( int i = 0; i < _elements.capacity(); ++i)
{
_elements.addElement(new String(""));
}
}
public String getElement(int id)
{
return (String)_elements.elementAt(id);
}
public void setElement(int id, String value)
{
_elements.setElementAt(value, id);
}
}

You should create objects that are expandable so that you can add fields in the future:
• You can store Boolean values as bits in an int. Reserve extra ones for future use.
• You can store Strings directly, but use a vector or hashtable of key/value pairs so that
additional (or seldom-used fields) can be added later.
• If you have indexes on a table, store them in a vector or array so that you can add further
indexes later.

Save an object
The following sample code demonstrates how to create a Save menu item that enables the user to
save information about a new restaurant. This code performs the following tasks:
• creates a RestaurantInfo object
• invokes the setElement() method onRestaurantInfo to set values of its vector components
• adds the RestaurantInfo object to the _data Vector
• invokes setContents and commit on the PersistentObject to save the updated data

90 BlackBerry Java Development Environment


Working with custom objects

private MenuItem saveItem = new MenuItem(


_resources, MENUITEM_SAVE, 110, 10) {
public void run() {

RestaurantInfo info = new RestaurantInfo();


info.setElement(RestaurantInfo.NAME, namefield.getText());
info.setElement(RestaurantInfo.ADDRESS,addressfield.getText());
info.setElement(RestaurantInfo.PHONE, phonefield.getText());
info.setElement(RestaurantInfo.SPECIALTY,
specialtyfield.getText());

_data.addElement(info)

synchronized(store) {
store.setContents(_data);
store.commit();
}
Dialog.inform(_resources.getString(APP_SUCCESS));
...
}
};

Tip: Synchronize on the persistent object when you make changes so that other threads cannot make changes
to the object at the same time.

Retrieve an object
The following code sample demonstrates how to create a Get menu item that enables the user to
retrieve a RestaurantInfo object to view the information. In this example, the Get menu invokes the
_data.lastElement() to retrieve the most recently saved RestaurantInfo object.

private MenuItem getItem = new MenuItem(_resources, MENUITEM_GET, 110, 11) {


public void run() {
synchronized(store) {
_data = (Vector)store.getContents();
if (!_data.isEmpty()) {
RestaurantInfo info = (RestaurantInfo)_data.lastElement();
namefield.setText(info.getElement(RestaurantInfo.NAME));
addressfield.setText(info.getElement(RestaurantInfo.ADDRESS));
phonefield.setText(info.getElement(RestaurantInfo.PHONE));
specialtyfield.setText(info.getElement(
RestaurantInfo.SPECIALTY));
}
}
}
};

Developer Guide 91
Chapter 5: Storing persistent data

Example: Restaurants.java

/**
* Restaurants.java
* Copyright (C) 2001-2003 Research In Motion Limited. All rights reserved.
*/

package com.rim.docs.samples.restaurants;

import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.system.*;
import net.rim.device.api.util.*;
import java.util.*;
import net.rim.device.api.i18n.*;
import com.rim.docs.samples.baseapp.*;

public class Restaurants extends BaseApp implements RestaurantResource,


KeyListener, TrackwheelListener {

private AutoTextEditField namefield;


private AutoTextEditField addressfield;
private EditField phonefield;
private EditField specialtyfield;

private static Vector _data;


private static PersistentObject store;
private static ResourceBundle _resources;

private MenuItem saveItem = new MenuItem(


_resources, MENUITEM_SAVE, 110, 10) {
public void run() {
RestaurantInfo info = new RestaurantInfo();

info.setElement(RestaurantInfo.NAME, namefield.getText());
info.setElement(RestaurantInfo.ADDRESS, addressfield.getText());
info.setElement(RestaurantInfo.PHONE, phonefield.getText());
info.setElement(RestaurantInfo.SPECIALTY,
specialtyfield.getText());

_data.addElement(info);

92 BlackBerry Java Development Environment


Working with custom objects

synchronized(store) {
store.setContents(_data);
store.commit();
}
Dialog.inform(_resources.getString(APP_SUCCESS));
namefield.setText(null);
addressfield.setText(null);
phonefield.setText("");
specialtyfield.setText("");
}
};

private MenuItem getItem = new MenuItem(_resources, MENUITEM_GET, 110, 11) {


public void run() {
synchronized(store) {
_data = (Vector)store.getContents();
if (!_data.isEmpty()) {
RestaurantInfo info = (RestaurantInfo)_data.lastElement();
namefield.setText(info.getElement(RestaurantInfo.NAME));
addressfield.setText(info.getElement(RestaurantInfo.ADDRESS));
phonefield.setText(info.getElement(RestaurantInfo.PHONE));
specialtyfield.setText(info.getElement(
RestaurantInfo.SPECIALTY));
}
}
}
};

static {
_resources = ResourceBundle.getBundle(
RestaurantResource.BUNDLE_ID,
RestaurantResource.BUNDLE_NAME);
store = PersistentStore.getPersistentObject(0xdec6a67096f833cL);
//key is hash of test.samples.restaurants
synchronized (store) {
if (store.getContents() == null) {
store.setContents(new Vector());
store.commit();
}
}
_data = new Vector();
_data = (Vector)store.getContents();
}

public static void main(String[] args) {


Restaurants app = new Restaurants();
app.enterEventDispatcher();
}

Developer Guide 93
Chapter 5: Storing persistent data

private static final class RestaurantInfo implements Persistable {

//data
private Vector _elements;

//fields
public static final int NAME = 0;
public static final int ADDRESS = 1;
public static final int PHONE = 2;
public static final int SPECIALTY = 3;

public RestaurantInfo()
{
_elements = new Vector(4); //initial capacity of 4
for ( int i = 0; i < _elements.capacity(); ++i)
{
_elements.addElement(new String(""));
}
}

public String getElement(int id)


{
return (String)_elements.elementAt(id);
}

public void setElement(int id, String value)


{
_elements.setElementAt(value, id);
}
}

public Restaurants()
{
MainScreen mainScreen = new MainScreen();
mainScreen.setTitle(new LabelField(
_resources.getString(APPLICATION_TITLE)));
namefield = new AutoTextEditField(
_resources.getString(FIELD_NAME), "");
addressfield = new AutoTextEditField(
_resources.getString(FIELD_ADDRESS), "");
phonefield = new EditField(
_resources.getString(FIELD_PHONE), "", Integer.MAX_VALUE,
BasicEditField.FILTER_PHONE);
specialtyfield = new EditField(
_resources.getString(FIELD_SPECIALTY), "",
Integer.MAX_VALUE, BasicEditField.FILTER_DEFAULT);

94 BlackBerry Java Development Environment


Working with custom objects

mainScreen.add(namefield);
mainScreen.add(addressfield);
mainScreen.add(phonefield);
mainScreen.add(specialtyfield);
mainScreen.addKeyListener(this);
mainScreen.addTrackwheelListener(this);
pushScreen(mainScreen);
}

public void makeMenu( Menu menu, int instance ) {


menu.add(saveItem);
menu.add(getItem);
super.makeMenu(menu, instance);
}

public void onExit()


{
Dialog.alert(_resources.getString(APP_EXIT));
}
}

Developer Guide 95
Chapter 5: Storing persistent data

96 BlackBerry Java Development Environment


Chapter 6
Backing up and restoring
persistent data
This section provides information on the following topics:
• Synchronization API
• Adding support for backing up data
• Creating an initialization project
• Testing an application in the simulator
Chapter 6: Backing up and restoring persistent data

Synchronization API
The synchronization API, in the net.rim.device.api.synchronization package, enables your
application to integrate with the BlackBerry Desktop Software to perform two tasks:
• back up a database to a desktop file and restore it later
• synchronize data with a desktop application

Backup
The BlackBerry Desktop Software provides a Backup and Restore tool that enables users to save
handheld data to a file on their desktop, and use this file to restore data to the handheld. For
example, users typically backup and restore handheld data before they upgrade handheld software.
Users can use the desktop software to back up all data, or specific application databases, from their
handheld to a desktop file, and later restore data to the handheld from the desktop.
When an application implements the synchronization API, the desktop software backs up and
restores the application database along with the other handheld databases. You can also use the
synchronization API to create data archives, or to populate application databases when the
handheld is first connected to the computer.
To back up handheld data, in the Backup and Restore tool, users click Backup. To restore handheld
data, users click Restore.
To back up or restore specific databases, users click the Advanced tab. The Advanced window
appears.

Backup and Restore tool – Advanced window

98 BlackBerry Java Development Environment


Synchronization API

Users can select one or more databases in the Handheld Databases list and move them to the
Desktop File Databases list to save data to a file. Users can select a database in the Desktop File
Databases list to restore data.

Tip: There are no restrictions on the format that you use to store data for backup. The only requirement is that
your application must use the same format to read data when it is restored as it uses to write data for backup.

Data synchronization
The desktop software provides an Intellisync tool to synchronize the handheld with the applications
on the user’s computer.
Whereas backup and restore performs a bulk load of data between the handheld and a desktop
backup file, synchronization compares the data that exists in a desktop application and on the
handheld and can merge the data.
To synchronize data with a desktop application, you must write a plug-in for the desktop software
using the BlackBerry Desktop API. For more information, refer to the Desktop API Reference Guide.
The BlackBerry JDE also includes a sample synchronization application with a desktop plug-in.

Tip: There are no restrictions on the format that you use to store data for backup. The only requirement is that
your handheld application read and write data in the same format that is used by your desktop plug-in
application.

Synchronization API overview


The synchronization API provides the following main interfaces that you must implement:

Interface Description

SyncConverter converts data between the SyncObject format required on the handheld and a serialized format
required on the desktop

SyncCollection represents the collection of synchronization objects for an application

SyncObject represents an object that can be backed up and restored to the user’s computer

The SerialSyncManager class provides access to the handheld synchronization manager, in


particular to register new objects for synchronization.

Note: To back up and restore a very small amount of data, such as application configuration options, you can
extend the SyncItem class and implement its abstract methods. The SyncItem class implements the
SyncCollection, SyncConverter, and SyncObject interfaces for you.

Developer Guide 99
Chapter 6: Backing up and restoring persistent data

Adding support for backing up data


The following example demonstrates how to enable desktop software to back up and restore your
application’s persistent data. This example modifies the Restaurants.java sample application to
implement the synchronization API. A complete code sample is provided at the end of this section.
Refer to "RestaurantsSync.java" on page 107 for more information.

Implement SyncObject
The Restaurants.java sample application defined a new type of object to store information about a
restaurant. This RestaurantInfo class implements the Persistable interface so that objects of this
class can be stored persistently on the handheld. This class must also implement the SyncObject
interface to support backup.
1. Modify the class that implements the Persistable interface so that it also implements the
SyncObject interface.

private static final class RestaurantInfo implements Persistable, SyncObject {

2. Define a _uid variable and implement getUID() to return a unique ID to use for synchronization
operations.

private int _uid;

public int getUID() {


return _uid;
}

3. Define a constructor that accepts a unique as a parameter and sets the _uid variable to this
value.

public RestaurantInfo(int uid) {


_elements = new Vector(4); //initial capacity of 4
for ( int i = 0; i < _elements.capacity(); ++i)
{
_elements.addElement(new String(""));
}
_uid = uid;
}

Note: Each synchronization object that is stored on the handheld must have an associated ID that is unique to
its application. The UIDGenerator sets this ID by default.

100 BlackBerry Java Development Environment


Adding support for backing up data

Implement SyncCollection
In the following example, SyncCollection and SyncConverter interfaces are implemented by the
same class. You can also separate the implementations, depending on the design of your application.
1. Import the package for the synchronization API.

import net.rim.device.api.synchronization.*;

2. Modify the main class for your application to implement the SyncCollection and
SyncConverter interfaces.

public class RestaurantsSync extends BaseApp implements RestaurantResource,


KeyListener, TrackwheelListener, SyncConverter, SyncCollection {

3. Define a Vector to manipulate data for backup and restore.

private Vector _data;

4. Define a variable and method to retrieve a new instance of the RestaurantsSync class.

private static RestaurantsSync _instance;

public static RestaurantsSync getInstance()


{
if (_instance == null) {
_instance = new RestaurantsSync();
}
return _instance;
}

5. Implement the SyncCollection methods for transactions.

public void beginTransaction() {


store.setContents(_data);
store.commit();
}

public void endTransaction() {


store = PersistentStore.getPersistentObject(KEY);
_data = (Vector)store.getContents();
}

6. Implement getSyncConverter() method to return an instance of this application, which


implements SyncConverter.

public SyncConverter getSyncConverter() {


return this;
}

Developer Guide 101


Chapter 6: Backing up and restoring persistent data

7. Implement the two versions of getSyncName() to provide a descriptive name for the
synchronization collection.

public String getSyncName() {


return "Restaurant Persistence Demo";
}

public String getSyncName(Locale locale)


{
return null; //localization not supported in this example
}

8. Implement getSyncObjectCount() to retrieve the number of synchronization objects in this


collection.

public int getSyncObjectCount() {


return _data.size();
}

9. Implement getSyncObjects() to retrieve all the synchronization objects in this collection.

public SyncObject[] getSyncObjects()


{
SyncObject[] array = new SyncObject[_data.size()];
for (int i = _data.size() - 1; i >= 0; --1)
{
array[i] = (SyncObject)_data.elementAt(i);
}
return array;
}

10. Implement getSyncObject() to retrieve a particular synchronization object in this collection.

public SyncObject getSyncObject(int uid)


{
for (int i = _data.size() - 1; i >= 0; --1)
{
SyncObject so = (SyncObject)_data.elementAt(i);
if (so.getUID() == uid) return so;
}
return null;
}

11. Implement getSyncVersion() to return the collection version number.

public int getSyncVersion()


{
return 1;
}

102 BlackBerry Java Development Environment


Adding support for backing up data

12. Implement removeAllSyncObjects() to remove all synchronization objects from the collection.
This method can be invoked when data is restored from the desktop.

public boolean removeAllSyncObjects() {


_data.removeAllElements();
return true;
}

13. Implement addSyncObject() to add a synchronization object to the collection. This method can
be called when data is restored.

public boolean addSyncObject(SyncObject object)


{
_data.addElement(object);
return true;
}

14. Implement the following methods without definitions. These methods are not required for
backup and restore, only for synchronization, which is not demonstrated in this example.

public boolean isSyncObjectDirty(SyncObject object)


{
return false; //not applicable
}

public boolean removeSyncObject(SyncObject object)


{
return false; //not applicable
}

public void setSyncObjectDirty(SyncObject object) {} //not applicable

public void clearSyncObjectDirty(SyncObject object) {} //not applicable

public boolean updateSyncObject(SyncObject oldObject, SyncObject newObject)


{
return false; //not applicable
}

Developer Guide 103


Chapter 6: Backing up and restoring persistent data

Implement SyncConverter
1. Add tags for each field in your persistent object.

private static final int FIELDTAG_NAME = 1;


private static final int FIELDTAG_PHONE = 2;
private static final int FIELDTAG_ADDRESS = 3;
private static final int FIELDTAG_SPECIALTY = 4;

2. Implement convert(SyncObject object, DataBuffer buffer, int version); this method is


called when data is backed up from the handheld. Serialize a SyncObject to send to the desktop.

public boolean convert(SyncObject object, DataBuffer buffer, int version) {


if (version == getSyncVersion()) {
if ( object instanceof RestaurantInfo )
{
String name = ((RestaurantInfo)object).getElement(
RestaurantInfo.NAME);
String phone = ((RestaurantInfo)object).getElement(
RestaurantInfo.PHONE);
String address = ((RestaurantInfo)object).getElement(
RestaurantInfo.ADDRESS);
String specialty = ((RestaurantInfo)object).getElement(
RestaurantInfo.SPECIALTY);
buffer.writeShort(name.length()+1);
buffer.writeByte(FIELDTAG_NAME);
buffer.write(name.getBytes());
buffer.writeByte(0);
buffer.writeShort(phone.length()+1);
buffer.writeByte(FIELDTAG_PHONE);
buffer.write(phone.getBytes());
buffer.writeByte(0);
buffer.writeShort(address.length()+1);
buffer.writeByte(FIELDTAG_ADDRESS);
buffer.write(address.getBytes());
buffer.writeByte(0);
buffer.writeShort(specialty.length()+1);
buffer.writeByte(FIELDTAG_SPECIALTY);
buffer.write(specialty.getBytes());
buffer.writeByte(0);
return true;
}
}
return false;
}

Tip: There are no restrictions on the format that you use to store data for backup, as long as your application
uses the same format to write data as it uses to read data.

104 BlackBerry Java Development Environment


Adding support for backing up data

3. Implement convert(DataBuffer data, int version, int UID). This method is called when
data is restored to the handheld from the desktop.

public SyncObject convert(DataBuffer data, int version, int UID)


{
try {
RestaurantInfo info = new RestaurantInfo(UID);
while(data.available() > 0) {
int length = data.readShort();
byte[] bytes = new byte[length];
switch (data.readByte()) {
case FIELDTAG_NAME:
data.readFully(bytes);
//trim null-terminator
info.setElement(RestaurantInfo.NAME,
new String(bytes).trim());
break;
case FIELDTAG_PHONE:
data.readFully(bytes);
info.setElement(RestaurantInfo.PHONE,
new String(bytes).trim());
break;
case FIELDTAG_ADDRESS:
data.readFully(bytes);
info.setElement(RestaurantInfo.ADDRESS,
new String(bytes).trim());
break;
case FIELDTAG_SPECIALTY:
data.readFully(bytes);
info.setElement(RestaurantInfo.SPECIALTY,
new String(bytes).trim());
break;
default:
data.readFully(bytes);
break;
}
}
return info;
} catch (EOFException e) {
System.err.println(e.toString());
}
return null;
}

Developer Guide 105


Chapter 6: Backing up and restoring persistent data

Register a synchronization collection


In the main() method of your application, register your SyncCollection with the handheld
synchronization manager. This requires that you create a separate project to pass in the init
argument when the handheld starts for the first time. Refer to "Creating an initialization project" on
page 106 for more information.
public static void main(String[] args) {
boolean startup = false;
for (int i=0; i<args.length; ++i) {
if (args[i].startsWith("init")) {
startup = true;
}
}

if (startup) {
//enable application for synchronization on startup
SerialSyncManager.getInstance().enableSynchronization(new
RestaurantsSync());
} else {
RestaurantsSync app = new RestaurantsSync();
app.enterEventDispatcher();
}
}

Creating an initialization project


To register a synchronization collection when the handheld starts, create a separate project that acts
as an alternate entry point to your main application. This passes an argument to your application
the first time that the handheld starts so that your application registers only once.
1. In the IDE, create a project.
2. Right-click the project and click Properties. The Properties window appears.
3. Click the Application tab.
4. From the Project type drop-down list, select Alternate CLDC Application Entry Point.
5. From the Alternate entry point for drop-down list, select the main project that implements
synchronization.
6. In the Arguments passed to field, type init.
7. Select the Auto-run on startup option.
8. Select the System module option.
9. Click OK.

106 BlackBerry Java Development Environment


Creating an initialization project

The following example demonstrates an application that enables persistent data to be backed up
and restored using the desktop software. Changes to the original Restaurants.java sample code to
appears in bold.

Example: RestaurantsSync.java

/**
* RestaurantsSync.java
* Copyright (C) 2001-2003 Research In Motion Limited. All rights reserved.
*/

package com.rim.docs.samples.restaurants;

import java.io.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.system.*;
import net.rim.device.api.util.*;
import java.util.*;
import net.rim.device.api.i18n.*;
import net.rim.device.api.synchronization.*;

import com.rim.docs.samples.baseapp.*;

public class RestaurantsSync extends BaseApp implements RestaurantsSyncResource,


SyncCollection, SyncConverter, KeyListener, TrackwheelListener {

private static final long KEY = 0xdec6a67096f833cL;

private AutoTextEditField namefield;


private AutoTextEditField addressfield;
private EditField phonefield;
private EditField specialtyfield;

private static PersistentObject store;


private static Vector _data;
private static ResourceBundle _resources;
private static final int FIELDTAG_NAME = 1;
private static final int FIELDTAG_PHONE = 2;
private static final int FIELDTAG_ADDRESS = 3;
private static final int FIELDTAG_SPECIALTY = 4;

private static RestaurantsSync _instance;

Developer Guide 107


Chapter 6: Backing up and restoring persistent data

private MenuItem saveItem = new MenuItem(_resources, MENUITEM_SAVE, 110, 10) {


public void run() {
RestaurantInfo info = new RestaurantInfo();
info.setElement(RestaurantInfo.NAME, namefield.getText());
info.setElement(RestaurantInfo.ADDRESS, addressfield.getText());
info.setElement(RestaurantInfo.PHONE, phonefield.getText());
info.setElement(RestaurantInfo.SPECIALTY, specialtyfield.getText());
_data.addElement(info);

synchronized(store) {
store.setContents(_data);
store.commit();
}
Dialog.inform(_resources.getString(APP_SUCCESS));
namefield.setText(null);
addressfield.setText(null);
phonefield.setText("");
specialtyfield.setText("");
}
};
private MenuItem getItem = new MenuItem(_resources, MENUITEM_GET, 110, 11) {
public void run() {
synchronized(store) {
_data = (Vector)store.getContents();
if (!_data.isEmpty()) {
RestaurantInfo info = (RestaurantInfo)_data.lastElement();
namefield.setText(info.getElement(RestaurantInfo.NAME));
addressfield.setText(info.getElement(RestaurantInfo.ADDRESS));
phonefield.setText(info.getElement(RestaurantInfo.PHONE));
specialtyfield.setText(info.getElement(
RestaurantInfo.SPECIALTY));
}
}
}
};

static {
_resources = ResourceBundle.getBundle(RestaurantsSyncResource.BUNDLE_ID,
RestaurantsSyncResource.BUNDLE_NAME);
store = PersistentStore.getPersistentObject(KEY);
synchronized (store) {
if (store.getContents() == null) {
store.setContents(new Vector());
store.commit();
}
}
_data = new Vector();
_data = (Vector)store.getContents();
}

108 BlackBerry Java Development Environment


Creating an initialization project

public static void main(String[] args) {


boolean startup = false;
for (int i=0; i<args.length; ++i) {
if (args[i].startsWith("init")) {
startup = true;
}
}

if (startup) {
//enable application for synchronization on startup
SerialSyncManager.getInstance().enableSynchronization(
RestaurantsSync.getInstance());
} else {
RestaurantsSync app = new RestaurantsSync();
app.enterEventDispatcher();
}
}

public static RestaurantsSync getInstance()


{
if (_instance == null) {
_instance = new RestaurantsSync();
}
return _instance;
}

private static final class RestaurantInfo implements Persistable, SyncObject {


private Vector _elements; //data

public static final int NAME = 0;


public static final int ADDRESS = 1;
public static final int PHONE = 2;
public static final int SPECIALTY = 3;

private int _uid;

public int getUID() {


return _uid;
}

public RestaurantInfo()
{
_elements = new Vector(4); //initial capacity of 4
for ( int i = 0; i < _elements.capacity(); ++i)
{
_elements.addElement(new String(""));
}
}

Developer Guide 109


Chapter 6: Backing up and restoring persistent data

public RestaurantInfo(int uid) {


_elements = new Vector(4); //initial capacity of 4
for (int i = 0; i < _elements.capacity(); ++i)
{
_elements.addElement(new String(""));
}
_uid = uid;
}

public String getElement(int id)


{
return (String)_elements.elementAt(id);
}

public void setElement(int id, String value)


{
_elements.setElementAt(value, id);
}
}

//synchronization - SyncConverter methods


public SyncObject convert(DataBuffer data, int version, int UID)
{
try {
RestaurantInfo info = new RestaurantInfo(UID);
while(data.available() > 0) {
int length = data.readShort();
byte[] bytes = new byte[length];
switch (data.readByte()) {
case FIELDTAG_NAME:
data.readFully(bytes);
//trim null-terminator
info.setElement(RestaurantInfo.NAME,
new String(bytes).trim());
break;
case FIELDTAG_PHONE:
data.readFully(bytes);
info.setElement(RestaurantInfo.PHONE,
new String(bytes).trim());
break;
case FIELDTAG_ADDRESS:
data.readFully(bytes);
info.setElement(RestaurantInfo.ADDRESS,
new String(bytes).trim());
break;

110 BlackBerry Java Development Environment


Creating an initialization project

case FIELDTAG_SPECIALTY:
data.readFully(bytes);
info.setElement(RestaurantInfo.SPECIALTY,
new String(bytes).trim());
break;
default:
data.readFully(bytes);
break;
}
}
return info;
} catch (EOFException e) {
System.err.println(e.toString());
}
return null;
}

public boolean convert(SyncObject object, DataBuffer buffer, int version)


{
if (version == getSyncVersion()) {
if (object instanceof RestaurantInfo )
{
String name = ((RestaurantInfo)object).getElement(
RestaurantInfo.NAME);
String phone = ((RestaurantInfo)object).getElement(
RestaurantInfo.PHONE);
String address = ((RestaurantInfo)object).getElement(
RestaurantInfo.ADDRESS);
String specialty = ((RestaurantInfo)object).getElement(
RestaurantInfo.SPECIALTY);
buffer.writeShort(name.length()+1);
buffer.writeByte(FIELDTAG_NAME);
buffer.write(name.getBytes());
buffer.writeByte(0);
buffer.writeShort(phone.length()+1);
buffer.writeByte(FIELDTAG_PHONE);
buffer.write(phone.getBytes());
buffer.writeByte(0);
buffer.writeShort(address.length()+1);
buffer.writeByte(FIELDTAG_ADDRESS);
buffer.write(address.getBytes());
buffer.writeByte(0);
buffer.writeShort(specialty.length()+1);
buffer.writeByte(FIELDTAG_SPECIALTY);
buffer.write(specialty.getBytes());
buffer.writeByte(0);
return true;
}
}

Developer Guide 111


Chapter 6: Backing up and restoring persistent data

return false;
}

public void beginTransaction() {


store = PersistentStore.getPersistentObject(KEY);
_data = (Vector)store.getContents();
}

public void endTransaction() {


store.setContents(_data);
store.commit();
}

public SyncConverter getSyncConverter() {


return this;
}
public String getSyncName() {
return "Restaurant Synchronization Demo";
}

public String getSyncName(Locale locale)


{
return getSyncName();
}

public int getSyncObjectCount() {


store = PersistentStore.getPersistentObject(KEY);
_data = (Vector)store.getContents();
return _data.size();
}

public SyncObject[] getSyncObjects()


{
SyncObject[] array = new SyncObject[_data.size()];
for (int i = _data.size() - 1; i >= 0; --i)
{
array[i] = (SyncObject)_data.elementAt(i);
}
return array;
}

public SyncObject getSyncObject(int uid)


{
for (int i = _data.size() -1; i>= 0; --i)
{
SyncObject so = (SyncObject)_data.elementAt(i);
if (so.getUID() == uid ) return so;
}

112 BlackBerry Java Development Environment


Creating an initialization project

return null;
}

public int getSyncVersion()


{
return 1;
}

public boolean addSyncObject(SyncObject object)


{
_data.addElement(object);
return true;
}

public boolean removeAllSyncObjects() {


_data.removeAllElements();
return true;
}

public void clearSyncObjectDirty(SyncObject object) {


//not applicable
}

public boolean isSyncObjectDirty(SyncObject object) { return false; }

public boolean removeSyncObject(SyncObject object) {


return false;
}
public void setSyncObjectDirty(SyncObject object) {}

public boolean updateSyncObject(SyncObject oldObject, SyncObject newObject) {


return false;
}

public RestaurantsSync()
{
MainScreen mainScreen = new MainScreen();
mainScreen.setTitle(new LabelField(
_resources.getString(APPLICATION_TITLE)));
namefield = new AutoTextEditField(_resources.getString(FIELD_NAME), "");
addressfield = new AutoTextEditField(
_resources.getString(FIELD_ADDRESS), "");
phonefield = new EditField(_resources.getString(FIELD_PHONE), "",
Integer.MAX_VALUE, BasicEditField.FILTER_PHONE);
specialtyfield = new EditField(_resources.getString(FIELD_SPECIALTY), "",
Integer.MAX_VALUE, BasicEditField.FILTER_DEFAULT);

Developer Guide 113


Chapter 6: Backing up and restoring persistent data

mainScreen.add(namefield);
mainScreen.add(addressfield);
mainScreen.add(phonefield);
mainScreen.add(specialtyfield);
mainScreen.addKeyListener(this);
mainScreen.addTrackwheelListener(this);
pushScreen(mainScreen);
}

public void makeMenu( Menu menu, int instance) {


menu.add(saveItem);
menu.add(getItem);
super.makeMenu(menu, instance);
}
public void onExit()
{
Dialog.alert(_resources.getString(APP_EXIT));
}
}

Testing an application in the simulator


To test an application that uses the synchronization API in the simulator, connect a null modem
cable between two COM ports so that the BlackBerry Desktop Software connects to the simulator.
1. Exit the BlackBerry Desktop Software.
2. Connect a null modem cable between COM port 1 and COM port 2 on your computer.
3. In the IDE, on the Edit menu, click Preferences. The Preferences window appears.
4. Click the Basic tab.
5. Select the Set serial port for device(S) option and type 1.
6. Click OK.
7. Build and run the application in the simulator.
8. After the simulator starts, start the BlackBerry Desktop Software.
9. In the BlackBerry Desktop Manager, on the Options menu, click Connection Settings.
10. Click Detect. A message appears that states that a handheld was found. The BlackBerry Desktop
Manager is now connected to the simulator.
If the BlackBerry Desktop Software does not detect the simulator, restart your computer and
repeat steps 7 through 10.

114 BlackBerry Java Development Environment


Chapter 7
Notifications
This section provides information on the following topics:
• Notification API
• Adding an event
• Responding to events
• Customizing system notifications
Chapter 7: Notifications

Notification API
The notification API, in the net.rim.device.api.notification package, enables you to add custom
events for your application, such as the arrival of a particular type of message, and define the type of
notification that the user receives when this event occurs.
There are two types of notification events:
• immediate events: handheld notification, such as flashing LED, vibration, or tune
• deferred events: application-specific notification, such as a user interface
With immediate events, the handheld provides notification to the user as soon as the event occurs,
using a system notification, such as a flashing LED, vibration, or tune. An application cannot
request a specific type of notification. In the handheld Profiles application, users control how they
are notified of immediate events by choosing an active profile and setting profile options. You can
implement the Consequence interface to add custom system notifications for immediate events, such
as a particular tune.
With deferred events, the handheld schedules events in a queue according to their priority. When
the event occurs, applications that are affected by the event can provide a custom notification to the
user, typically by displaying a user interface such as a dialog box. Applications must implement the
NotificationsEngineListener to listen for deferred events. The handheld does not provide
system-wide notification for deferred events.
The following illustration shows the window that appears when a user opens a particular
notification item in the Default profile.

Notification item edit window in Default profile

116 BlackBerry Java Development Environment


Adding an event

Adding an event
To add a new source event to the handheld for notifications, complete the following tasks:
• register the event with the handheld
• implement methods in your application to trigger the event

To register a new event source


1. Define a long ID for each notification event.

public static final long ID_1 = 0xdc5bf2f81374095L;

Tip: You can use the IDE to convert a String to a long to create a similar identifier for your application.
1. In the IDE text pane, type a string.
2. Select the string.
3. Right-click and click Convert “string” to Long.

2. Define an object that provides the source for the event. This object must implement a
toString() method that returns the string to display in the Profiles application.

Object event = new Object() {


public String toString() {
return "Notification Demo"
}
}

3. Invoke NotificationsManager.registerSource() to add your application to the handheld


Profiles application as the source of an event. In this method, specify a unique event ID, the
source object, and the notification level.

NotificationsManager.registerSource(ID_1, event,
NotificationsConstants.CASUAL);

The notification level sets the priority of the event, which determines the order in which
deferred events occur. The levels, in order from highest to lowest priority, are as follows:
• NotificationsConstants.CRITICAL
• NotificationsConstants.SENSITIVE
• NotificationsConstants.IMPORTANT
• NotificationsConstants.DEFAULT_LEVEL
• NotificationsConstants.CASUAL

Developer Guide 117


Chapter 7: Notifications

Note: The priority level applies to deferred events only. Immediate events occur as soon as they are triggered.
When you trigger a deferred event, you specify an expiry time. It is possible that the user will not receive
notification of a lower-priority event, if the event expires before higher-priority events complete.

When registerSource() is invoked, a new event source is added to the handheld Profiles
application.

Default profile window – list of notification items

Create a library project


To register an event source, create a library project with a libMain() method to perform the
registration when the handheld starts.
1. In the IDE, create a project.
2. Right-click the project and click Properties. The Properties window appears.
3. Click the Application tab.
4. From the Project type drop-down list, select Library.
5. Select the Auto-run on startup option.
6. Click OK.
In this project, define a libMain() method as in the following example:
public static final long ID_1 = 0xdc5bf2f81374095L;
public static final Object event = new Object() {
public String toString() { return "Sample Notification Event #1"; }
};
public static void libMain(String[] args)
{
NotificationsManager.registerSource(ID_1, event,
NotificationsConstants.CASUAL);
}

118 BlackBerry Java Development Environment


Adding an event

Trigger an immediate event


To trigger an immediate event, invoke triggerImmediateEvent(). Immediate events are intended
for standard system notifications, such as tune, vibration, or LED.
NotificationsManager.triggerImmediateEvent(ID_1, 0, this, null);

The triggerImmediateEvent method accepts four parameters:

Parameter Description
sourceID identifier of the application that starts the event (as specified when you invoked
registerSource())

eventID application-specific event identifier

eventReference application-specific event cookie

context optional context object

In most cases, you should not use immediate events, because the handheld event notification does
not adequately indicate to the user what has happened. For example, if the handheld vibrates, it
would be difficult for the user to know whether an event has occurred in your application, or
whether a new email message has arrived. If you do use immediate events, you should consider
implementing a custom notification, such as a particular tune, to distinguish your application’s
events from other handheld events. Refer to "Customizing system notifications" on page 126 for
more information.

Trigger a deferred event


To trigger a deferred event, invoke negotiateDeferredEvent(). A deferred event enables your
application to notify the user with a user interface, such as a dialog box.
NotificationsManager.negotiateDeferredEvent(ID_1, 0, this, -1,
NotificationsConstants.MANUAL_TRIGGER, null);

Developer Guide 119


Chapter 7: Notifications

The negotiateDeferredEvent method accepts the following parameters:

Parameter Description
sourceID identifier of the application that starts the event (as specified when you invoked
registerSource())

eventID application-specific event identifier

eventReference application-specific event cookie

timeout event expiry time, in milliseconds, relative to time when the method is invoked (the timeout is
ignored unless the trigger is OUT_OF_HOLSTER_TRIGGER)

trigger either NotificationsConstants.OUT_OF_HOLSTER_TRIGGER, which specifies that the event


occurs when the handheld is disconnected from the computer; or
NotificationsConstants.MANUAL_TRIGGER, which specifies that the application itself triggers
this event

context optional object that can store additional, arbitrary parameters to control the state or behavior of
an event notification

If you invoke negotiateDeferredEvent(), your application must implement the


NotificationEventListener to receive events and respond appropriately. Refer to "Responding to
events" on page 121 for more information.

Cancel events
To cancel an immediate event, invoke cancelImmediateEvent(), and specify the source and event
IDs:
NotificationsManager.cancelImmediateEvent(ID_1, 0, this, null);

To cancel a deferred event, invoke cancelDeferredEvent() and specify the source and event IDs:
NotificationsManager.cancelDeferredEvent(ID_1, 0, this,
NotificationsConstants.MANUAL_TRIGGER, null);

You can also cancel all deferred events that your application started:
NotificationsManager.cancelAllDeferredEvents(ID_1,
NotificationsConstants.MANUAL_TRIGGER, null);

Tip: If you invoke negotiateDeferredEvent() and do not specify a timeout, you must invoke
cancelDeferredEvent() to cancel the event, or the event never expires.

120 BlackBerry Java Development Environment


Responding to events

Responding to events
If you invoke negotiateDeferredEvent(), you must implement and register the
NotificationsEngineListener. You do not need to implement the listener if you trigger immediate
events, for which the handheld provides system notification.

Implement notifications listener


Implement NotificationsEngineListener to provide a custom UI notification to the user when a
deferred event occurs. For more information on creating user interfaces, refer to the
BlackBerry Developer Guide Volume 1 – Fundamentals.
1. Implement the NotificationsEngineListener interface.

private static class ListenerImpl implements NotificationsEngineListener {

2. Provide a constructor for your listener class. In the following example, declare a UiApplication
object to use to display a dialog box.

private UiApplication _app;

public NotificationsEngineListenerImpl(UiApplication app) {


_app = app;
}

3. Implement deferredEventWasSuperseded(). This method is invoked when the event is


superseded by another event at the same or higher priority level. For example, you could cancel
the event if it is superseded.

public void deferredEventWasSuperseded(long sourceID, long eventID,


Object eventReference, Object context) {
final long _eventID = eventID;
er = eventReference;
_app.invokeLater(new Runnable() {
public void run() {
NotificationsManager.cancelDeferredEvent(ID_1, _eventID, er,
NotificationsConstants.MANUAL_TRIGGER, null);
}
});
}

4. Implement notificationsEngineStateChanged(). This method is invoked when the handheld


is placed in, or removed from, the holster. For example, you could perform a specific action
when a deferred event is scheduled and the handheld is connected to, or disconnected from, the
computer.

Developer Guide 121


Chapter 7: Notifications

public void notificationsEngineStateChanged(int stateInt,


long sourceID, long eventID, Object eventReference, Object context) {

if(stateInt == otificationsConstants.OUT_OF_HOLSTER_ENGINE_STATE) {
//perform action if handheld is removed from holster
}

if(stateInt == NotificationsConstants.IN_HOLSTER_ENGINE_STATE) {
//perform action if handheld is inserted into holster
}
}

5. Implement proceedWithDeferredEvent() to define how to notify the user when the event
occurs. This method is invoked when the listener proceeds with the event (no other higher
priority events are in the queue). The following example demonstrates how to display a dialog
box to the user.

public void proceedWithDeferredEvent(long sourceID,


long eventID, Object eventReference, Object context) {
final long _eventID = eventID;
_app.invokeLater(new Runnable() {
public void run() {
String s = "This event has occurred: " + _eventID;
Dialog d = new Dialog(Dialog.D_OK, s, Dialog.OK,
Bitmap.getPredefinedBitmap(Bitmap.INFORMATION), 0);
d.show();
_eventHashtable.put(_eventID, d);
}
});
}
}

Register the notifications listener


Register your listener with the NotificationsManager. Specify the event source ID of your
application and an instance of the class that implements NotificationsEngineListener.
NotificationsManager.registerNotificationsEngineListener(
ID_1, new ListenerImpl(this));

Note: You can only register one NotificationsEngineListener with an application.

122 BlackBerry Java Development Environment


Responding to events

The following example extends the BaseApp class. Refer to "Base class for UI applications" on page
156 for more information.

Example: NotificationsDemo.java

/**
* NotificationsDemo.java
* Copyright (C) 2001-2003 Research In Motion Limited. All rights reserved.
*/

package com.rim.docs.samples.notifications;

import net.rim.device.api.notification.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.system.*;
import net.rim.device.api.util.*;
import com.rim.docs.samples.baseapp.*;

public class NotificationsDemo extends BaseApp


{

public static final long ID_1 = 0xdc5bf2f81374095L;


private long _eventIdGenerator;
private static Object er;

public static final Object event = new Object() {


public String toString() {
return "Sample Notification Event #1";
}
};

public static void main(String[] args)


{
NotificationsManager.registerSource(ID_1, event,
NotificationsConstants.CASUAL);
NotificationsManager.registerConsequence(ConsequenceDemo.ID,
new ConsequenceDemo());
NotificationsDemo app = new NotificationsDemo();
app.enterEventDispatcher();
}

Developer Guide 123


Chapter 7: Notifications

public NotificationsDemo() {
MainScreen mainScreen = new MainScreen();
mainScreen.setTitle("Notification Demo App");
mainScreen.addKeyListener(this);
mainScreen.addTrackwheelListener(this);
NotificationsManager.registerNotificationsEngineListener(ID_1,
new NotificationsEngineListenerImpl(this));
pushScreen(mainScreen);
}

private MenuItem triggerItem = new MenuItem(null, 0, 100, 10) {


public void run() {
NotificationsManager.triggerImmediateEvent(ID_1, 0, this, null);
}
public String toString() {
return "Trigger event";
}
};

private MenuItem deferItem = new MenuItem(null, 0, 100, 10) {


public void run() {
long timeout = -1; //ignored unless trigger is OUT_OF_HOLSTER_TRIGGER
int trigger = NotificationsConstants.MANUAL_TRIGGER;
Object er = new Object();
NotificationsManager.negotiateDeferredEvent(ID_1, ++_eventIdGenerator,
er, timeout, trigger, null);
}
public String toString() {
return "Start deferred event";
}
};
private MenuItem cancelItem = new MenuItem(null, 0, 100, 10) {
public void run() {
int trigger = NotificationsConstants.MANUAL_TRIGGER;
NotificationsManager.cancelDeferredEvent(ID_1, _eventIdGenerator, er,
trigger, null);
}
public String toString() {
return "Cancel deferred event";
}
};

public void makeMenu( Menu menu, int instance) {


menu.add(triggerItem);
menu.add(deferItem);
menu.add(cancelItem);
super.makeMenu(menu, instance);
}

124 BlackBerry Java Development Environment


Responding to events

public void onExit() {


System.exit(0);
}

private static class NotificationsEngineListenerImpl implements


NotificationsEngineListener {
private UiApplication _app;
public NotificationsEngineListenerImpl(UiApplication app) {
_app = app;
}

public void deferredEventWasSuperseded(long sourceID, long eventID,


Object eventReference, Object context) {
final long _eventID = eventID;
er = eventReference;
_app.invokeLater(new Runnable() {
public void run() {
NotificationsManager.cancelDeferredEvent(ID_1, _eventID, er,
NotificationsConstants.MANUAL_TRIGGER, null);
}
});
}
public void notificationsEngineStateChanged(int stateInt, long sourceID,
long eventID, Object eventReference, Object context) {
if(stateInt == NotificationsConstants.OUT_OF_HOLSTER_ENGINE_STATE) {
//perform some action if handheld is removed from holster
}
if(stateInt == NotificationsConstants.IN_HOLSTER_ENGINE_STATE) {
//perform some action if handheld is inserted into holster
}
}
public void proceedWithDeferredEvent(long sourceID, long eventID,
Object eventReference, Object context) {
final long _eventID = eventID;
_app.invokeLater(new Runnable() {
public void run() {
String s = "This event has occurred: " + _eventID;
Dialog d = new Dialog(Dialog.D_OK, s, Dialog.OK,
Bitmap.getPredefinedBitmap(Bitmap.INFORMATION), 0);
d.show();
}
});
}
}
}

Developer Guide 125


Chapter 7: Notifications

Customizing system notifications


You can implement the Consequence interface to create custom system notifications on the
handheld, such as a particular tune, for immediate events. You can also implement Consequence to
perform other actions when events occur, such as to create a log that counts the number of various
notifications received.

Note: The Consequence interface is used only for immediate events that require system notification. Deferred
events require your application to implement the NotificationsEngineListener and respond with an
application-specific response.

You are responsible for providing your own application on the handheld Home screen, similar to
the handheld Profiles application, to enable users to set notification options.

Implement a consequence
1. Implement the Consequence and SyncConverter interfaces.

private static class ConsequenceImpl implements Consequence,


SyncConverter {

Note: SyncConverter is required to enable the handheld to back up and restore profile configurations. Refer to
"Backing up and restoring persistent data" on page 97 for more information.

2. Define a unique ID for this consequence.

public static final long ID = 0xbd2350c0dfda2a51L;

3. Declare DATA and TYPE constants for the application. These constants are used when identifying
the type of incoming data from the SyncConverter when convert() is invoked. They are
arbitrary identifiers for data appropriate to this application.

private static final int TYPE = 'n' << 24 | 'o' << 16 | 't' << 8 | 'd';
private static final byte[] DATA = new byte[] {'m', 'y', '-', 'c',
'o', 'n', 'f', 'i', 'g', '-', 'o', 'b', 'j', 'e', 'c', 't'};

private static final Configuration CONFIG = new Configuration(DATA);

4. Create a tune to be played as part of the consequence for event notifications.

private static final short BFlat = 466; //466.16


private static final short TEMPO = 125;
private static final short d16 = 1 * TEMPO;
private static final short dpause = 10; //10 millisecond pause
private static final short[] TUNE = new short[] {BFlat, d16, pause, BFlat};
private static final int VOLUME = 80; //percentage volume

126 BlackBerry Java Development Environment


Customizing system notifications

5. Implement startNotification() to define the notification for this consequence. In the


following code sample, the LED flashes and a tune is played.

public void startNotification(long consequenceID, long sourceID,


long eventID, Object configuration, Object context) {

LED.setConfiguration(500, 250, LED.BRIGHTNESS_50);


LED.setState(LED.STATE_BLINKING);

Alert.startAudio(TUNE, VOLUME);
Alert.startBuzzer(TUNE, VOLUME);
}

6. Implement stopNotification() to stop the notification for this consequence.

public void stopNotification(long consequenceID, long sourceID,


long eventID, Object configuration, Object context) {
LED.setState(LED.STATE_OFF);
Alert.stopAudio();
Alert.stopBuzzer();
}

7. Implement newConfiguration() to create a new configuration object that stores the user’s
profile settings. This object is passed to the consequence implementation to determine whether
the type of consequence that the user specified is appropriate for the event. The following
example returns the CONFIG object that you defined earlier.

public Object newConfiguration(long consequenceID, long sourceID,


byte profileIndex, int level, Object context) {
return CONFIG;
}

8. Implement SyncConverter.convert(DataBuffer data, int version, int UID). This method is


invoked when data is backed up from the handheld to the user’s computer. The following
example reads incoming data from the DataBuffer and applies a four-byte type and length to
the raw data.

public SyncObject convert(DataBuffer data, int version, int UID) {


try {
int type = data.readInt();
int length = data.readCompressedInt();
if ( type == TYPE ) {
byte[] rawdata = new byte[length];
data.readFully(rawdata);
return new Configuration(rawdata);
}

Developer Guide 127


Chapter 7: Notifications

} catch (EOFException e) {
System.err.println(e);
}
return null;
}

9. Implement SyncConverter.convert(SyncObject object, DataBuffer buffer, int version).


This method is invoked when data is restored from the user’s computer to the handheld.

public boolean convert(SyncObject object, DataBuffer buffer,


int version) {
boolean retval = false;
if ( object instanceof Configuration ) {
Configuration c = (Configuration)object;
buffer.writeInt(TYPE);
buffer.writeCompressedInt(c._data.length);
buffer.write(c._data);
retval = true;
}
return retval;
}

10. Create a class that describes the notification configuration information. The class implements
SyncObject and Persistable. You must implement SyncObject.getUID() but you can return 0
because this value is required only for data synchronization, which is not required in this
example.

private static final class Configuration implements SyncObject, Persistable {


public byte[] _data;
public Configuration(byte[] data) {
_data = data;
}
public int getUID() {
return 0;
}
}

Register a consequence
If you create a custom Consequence implementation, register it with the NotificationsManager
using the registerNotificationsObjects() method.
NotificationsManager.registerConsequence(ConsequenceImpl.ID,
new ConsequenceImpl());

You would typically perform this registration in a library project, so that the consequence is
registered when the handheld starts. Refer to "Create a library project" on page 118 for more
information.

128 BlackBerry Java Development Environment


Customizing system notifications

Example: ConsesquenceDemo.java

/**
* ConsequenceDemo.java
* Copyright (C) 2001-2003 Research In Motion Limited. All rights reserved.
*/

package com.rim.docs.samples.notifications;

import net.rim.device.api.synchronization.*;
import net.rim.device.api.notification.*;
import net.rim.device.api.system.*;
import net.rim.device.api.util.*;
import java.io.*;

public class ConsequenceDemo implements Consequence, SyncConverter {

public static final long ID = 0xbd2350c0dfda2a51L;


private static final int TYPE = 'n' << 24 | 'o' << 16 | 't' << 8 | 'd';
private static final byte[] DATA = new byte[] {
'm', 'y', '-', 'c', 'o', 'n', 'f', 'i',
'g', '-', 'o', 'b', 'j', 'e', 'c', 't' };

private static final Configuration CONFIG = new Configuration(DATA);

private static final short BFlat = 466; //466.16


private static final short TEMPO = 125;
private static final short d16 = 1 * TEMPO;
private static final short pause = 10; //10 millisecond pause
private static final short[] TUNE = new short[] {BFlat, d16, pause, BFlat};
private static final int VOLUME = 80; //percentage volume

public void startNotification(long consequenceID, long sourceID, long eventID,


Object configuration, Object context) {
LED.setConfiguration(500, 250, LED.BRIGHTNESS_50);
LED.setState(LED.STATE_BLINKING);

Alert.startAudio(TUNE, VOLUME);
Alert.startBuzzer(TUNE, VOLUME);
}

public void stopNotification(long consequenceID, long sourceID, long eventID,


Object configuration, Object context) {
LED.setState(LED.STATE_OFF);

Developer Guide 129


Chapter 7: Notifications

Alert.stopAudio();
Alert.stopBuzzer();
}

public Object newConfiguration(long consequenceID, long sourceID,


byte profileIndex, int level, Object context) {
return CONFIG;
}

public SyncObject convert(DataBuffer data, int version, int UID) {


try {
int type = data.readInt();
int length = data.readCompressedInt();
if ( type == TYPE ) {
byte[] rawdata = new byte[length];
data.readFully(rawdata);
return new Configuration(rawdata);
}
} catch (EOFException e) {
System.err.println(e);
}
return null;
}
public boolean convert(SyncObject object, DataBuffer buffer, int version) {
boolean retval = false;
if ( object instanceof Configuration ) {
Configuration c = (Configuration)object;
buffer.writeInt(TYPE);
buffer.writeCompressedInt(c._data.length);
buffer.write(c._data);
retval = true;
}
return retval;
}

/*inner class to store configuration profile */


private static final class Configuration implements SyncObject, Persistable {

public byte[] _data;

public Configuration(byte[] data) {


_data = data;
}
public int getUID() {
return 0;
}
}
}

130 BlackBerry Java Development Environment


Chapter 8
Using UDP connections
This section provides information on the following topics:
• Datagram UDP connections
• Using UDP connections
Chapter 8: Using UDP connections

UDP connections
The BlackBerry handheld supports datagram connections using the User Datagram Protocol (UDP).
Using UDP, your application can communicate with standard network services.
Datagrams are independent packets of data that your application can send over the network. Unlike
HTTP connections, datagram connections are stateless: packets can arrive in any order, and delivery
is not guaranteed. Your application is responsible for formatting the data payload of request
datagrams to conform to the standards of the network service with which it is communicating. Your
application must also be able to parse the datagrams that are sent back from the server.
To use a UDP connection, you must have your own infrastructure to connect to the wireless
network, including an access point name (APN) for GPRS networks.

Note: Datagram connections do not use the BlackBerry network infrastructure, so communication is not
encrypted.

The javax.microedition.io.DatagramConnection interface, which extends Connection, defines


connections that send and receive datagrams. The Datagram interface defines the packets that are
sent and received over a datagram connection.

Note: Using UDP connections requires that you work closely with service providers. Contact your
service provider to verify that UDP connections are supported.

Using UDP connections


To open a UDP connection, invoke the Connector.open method using the following format,
specifying udp as the protocol:
(DatagramConnection)Connector.open("udp://host:dest_port[;src_port]/apn");

The following table explains each parameter.

Parameter Description Default

host destination host address to which to send data, in dotted decimal format, such as —
112.11.11.11

dest_port destination port at the host address to which to send data —

src_port source port on the handheld on which to receive incoming data dest_port

apn APN to use to connect to the network, in string format (for GPRS networks only) —

132 BlackBerry Java Development Environment


Using UDP connections

Note: To test an application in the simulator, add the following command line option to open a port for
listening: /rport=src_port. To add this option in the IDE, on the Edit menu, click Preferences. Click the
Simulator tab and the Basic subtab. Select the Socket port and apn option and type the source port value.

Open a connection
To open a UDP connection, retrieve a DatagramConnection object by invoking the Connector.open
method. Specify udp as the protocol, as shown in the following example:
private static String address = "udp://121.0.0.0:2332;6343/test_apn";
DatagramConnection conn = null;
conn = (DatagramConnection)Connector.open(address);

In this example, a destination host and port are specified to send data to the far end of the
connection, and a source port is specified for receiving data on the handheld.
For inbound-only connections, you can omit the destination port, in which case the connection can
receive data from all ports at the specified host.

Note: To open a connection on a non-GPRS network, do not specify the APN. You must still include the slash
mark after the source port. For example, the address for a CDMA network connection would be
udp://121.0.0.0:2332;6343/.

Send data
Create a datagram by invoking the newDatagram() method on the DatagramConnection object. This
method returns a Datagram object.
1. Create a Datagram object by invoking the DatagramConnection.newDatagram method. For
example, the following code sample demonstrates how to create a Datagram with an
automatically allocated buffer.

byte[] buf = new byte[256];


Datagram outDatagram = conn.newDatagram(buf, buf.length);

2. Add payload data using the Datagram.setData() method.

//set the payload for the datagram that is formatted


//correctly for the service that receives it

3. Call the send method on DatagramConnection to send the datagram.

conn.send(outDatagram);

Note: If you attempt to send a datagram on a UDP connection and you are not listening on the specified source
port, an IOException is thrown.

Developer Guide 133


Chapter 8: Using UDP connections

Receive data
To receive a datagram, call the receive() method on the datagram connection:
byte[] buf = new byte[256];
Datagram inDatagram = conn.newDatagram(buf, buf.length);
conn.receive(inDatagram);

The receive() method blocks other operations until it receives a packet. If the packet is lost, the
application waits indefinitely. As a result, you should typically set a timer to retransmit the request
or close the connection if a reply does not arrive.
Use the getData() method to extract data from the packet. If you know the type of data that you are
receiving, you can convert the data to the appropriate format:
String received = new String(inDatagram.getData());

Close the connection


As with all connections in the MIDP framework, invoke the close() method on input and output
streams to close them, and then invoke close() on the connection to close it:
conn.close();

134 BlackBerry Java Development Environment


Chapter 9
Using SMS
This section provides information on the following topics:
• SMS overview
• Opening a network connection
• Sending an SMS message
• Receiving an SMS message
• Testing an SMS application
Chapter 9: Using SMS

SMS overview
SMS messages are sent and received as datagrams. Datagrams are independent packets of data that
your application can send over the network. Unlike HTTP connections, datagram connections are
stateless: packets can arrive in any order, and delivery is not guaranteed.
An SMS message is sent in a datagram packet using the User Datagram Protocol (UDP). The
datagram packet, which includes the BlackBerry header information, has a fixed size of 160 bytes.

Note: SMS messaging is not fully supported on all networks. Check with your service provider to verify that the
relevant networks have full or partial support for SMS messaging. SMS is supported on GPRS and CDMA
network-enabled handhelds, in most cases.

If the service provider supports SMS, system administrators can also use an IT policy to control the use of SMS
messaging by corporate users. Administrators can set the ENABLE_SMS item to TRUE or FALSE. The default is
TRUE (SMS messaging is allowed).

Opening a network connection


Create a DatagramConnection. Invoke Connector.open() to open a connection across the wireless
network to send and receive SMS messages: Use the following format:
private DatagramConnection _dc;
_dc = (DatagramConnection)Connector.open("sms://<peer_address><port>");

where:
• <peer_address> is the phone number—Mobile Station ISDN Number (MSISDN)—of the
sender or recipient (required for sending an SMS message, but optional for receiving)
• <port> is the port number for which the application receives SMS messages (optional, only
used for receiving SMS messages)
Typically, you do not specify the peer_address and port parameters when you open the connection.
The following code opens a standard connection for sending and receiving SMS messages:
Connector.open("sms://");

This code opens a connection that can receive all SMS messages sent to the handheld. When you
send an SMS message, invoke Datagram.setAddress() to set the destination address of the message.

136 BlackBerry Java Development Environment


Sending an SMS message

Sending an SMS message


Note: You must open network connections on a separate thread from the main application thread. Refer to the
BlackBerry Java Developer Guide Volume 1 – Fundamentals for more information on networking.

1. Create a Datagram object by invoking newDatagram() on the datagram connection. The following
example creates a datagram that is the maximum length allowed, with an automatically
allocated buffer:

Datagram datagram = _dc.newDatagram(_dc.getMaximumLength());

2. Invoke the Datagram.setAddress method to set the SMS address.

datagram.setAddress("sms://+15555551234");

3. Invoke the Datagram.setData method to set SMS message contents.

private String _msg = "This is a test message";


byte[] data = _msg.getBytes();
datagram.setData(data, 0, data.length);

4. Invoke the DatagramConnection.send() method to send the SMS message.

_dc.send(datagram);

The following code sample, SendSms.java, demonstrates how to receive an SMS message on a
separate thread. For a complete example, refer to the SmsDemo.java sample application included in
the BlackBerry JDE sample workspace.

Developer Guide 137


Chapter 9: Using SMS

Example: SendSms.java

/**
* SendSms.java
* Copyright (C) 2002-2003 Research In Motion Limited. All rights reserved.
*/

package com.rim.docs.samples.sendsms;

import net.rim.device.api.io.*;
import net.rim.device.api.system.*;
import javax.microedition.io.*;
import java.util.*;
import java.io.*;

public class SendSms extends Application


{
//constants
private static final int MAX_PHONE_NUMBER_LENGTH = 32;

//members
private String addr = "15191112222";
private String msg = "This is a test message.";
private DatagramConnection _dc = null;
private static String _openString = "sms://";

public static void main(String[] args)


{
new SendSms().enterEventDispatcher();
}

public SendSms()
{
try {
_dc = (DatagramConnection)Connector.open(_openString);
byte[] data = msg.getBytes();
Datagram d = _dc.newDatagram(_dc.getMaximumLength());
d.setAddress("//" + addr);
_dc.send(d);
} catch ( IOException e) {
//
}
System.exit(0);
}
}

138 BlackBerry Java Development Environment


Receiving an SMS message

Receiving an SMS message


Invoke the DatagramConnection.receive() method to receive an SMS message. Typically, you
would create a separate thread to listen for incoming SMS messages. Refer to the BlackBerry Java
Developer Guide Volume 1 – Fundamentals for more information on threads.
1. Create a new Datagram object.

Datagram d = _dc.newDatagram(_dc.getMaximumLength());

2. Invoke receive() on the datagram connection to retrieve the SMS message datagram. This
operation blocks until data is received.

_dc.receive(d);

3. Invoke Datagram.getAddress() to extract the address from the SMS message.

String address = d.getAddress();

4. Invoke Datagram.getData() to extract the data from the SMS message.

String msg = new String(d.getData());

The following code sample, ReceiveSms.java, demonstrates how to receive an SMS message on a
separate thread. For a complete example, refer to the SmsDemo.java sample application included in
the BlackBerry JDE sample workspace.

Developer Guide 139


Chapter 9: Using SMS

Example: ReceiveSms.java

/**
* ReceiveSms.java
* Copyright (C) 2002-2003 Research In Motion Limited. All rights reserved.
*/

package com.rim.docs.samples.smsdemo;

import net.rim.device.api.io.*;
import net.rim.device.api.system.*;
import javax.microedition.io.*;
import java.util.*;
import java.io.*;

public class ReceiveSms extends Application {

private ListeningThread _listener;

//additional code required for complete sample

public static void main(String[] args)


{
new ReceiveSms().enterEventDispatcher();
}

ReceiveSms() {
_listener = new ListeningThread();
_listener.start();
}

private class ListeningThread extends Thread


{
private boolean _stop = false;
private DatagramConnection _dc;
public synchronized void stop()
{
_stop = true;
try {
_dc.close(); //close the connection so the thread will return.
} catch (IOException e) {
System.err.println(e.toString());
}
}

140 BlackBerry Java Development Environment


Testing an SMS application

public void run()


{
try {
_dc = (DatagramConnection)Connector.open("sms://");
for(;;)
{
if ( _stop ) {
return;
}
Datagram d = _dc.newDatagram(_dc.getMaximumLength());
_dc.receive(d);
String address = new String(d.getData());
String msg = new String(d.getData());
System.out.println("Message received: " + msg);
System.out.println("From: " + address);
System.exit(0);
}
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
}

Testing an SMS application


The SmsDemo.java sample application in the samples workspace requires a server-side application
to interact with the handheld simulator, to simulate sending and receiving SMS messages. You
cannot send an actual SMS message from the simulator on your desktop computer.
A server-side component of the sample application (SMSServer.java) is included with the
BlackBerry JDE. To run the server side component, open run.bat in
<root>\samples\com\rim\samples\server\smsdemo\.

Developer Guide 141


Chapter 9: Using SMS

142 BlackBerry Java Development Environment


Chapter 10
Managing applications
This section provides information on the following topics:
• Working with the application manager
• Working with code modules
• Managing code modules
Chapter 10: Managing applications

Working with the application manager


The virtual machine (VM) on the BlackBerry Wireless Handheld has an application manager, which
functions as the central dispatcher of operating system events for other Java applications.
The net.rim.device.api.system.ApplicationManager class enables applications to interact with
the application manager to perform the following tasks:
• interact with processes, such as retrieving the IDs for foreground applications
• post global events to the system
• lock or unlock the handheld, or determine whether the handheld is locked
• run an application immediately or at a specific time
To use any of the ApplicationManager methods, you must first retrieve a reference to the current
application manager using the getApplicationManager() method:
ApplicationManager manager = ApplicationManager.getApplicationManager();

Retrieve information about applications


You can retrieve information about running processes by invoking getVisibleApplications() on
the ApplicationManager. For example, using the ApplicationManager, you could write an IT
application that audits the state of the handheld to determine which applications users are using
and for how long.
The getVisibleApplications() method retrieves an array of ApplicationDescriptor objects for
visible applications that are running. An ApplicationDescriptor contains descriptive information
for the application, such as its name, icon, position on the Home screen, resource bundle, and so on.
Use ApplicationDescriptor methods to retrieve this information.
For example, the following code sample demonstrates how to retrieve the name of a process.
ApplicationManager manager = ApplicationManager.getApplicationManager();
ApplicationDescriptor descriptors[] = manager.getVisibleApplications();
String appname1 = descriptors[0].getName();

Alternatively, you can retrieve the name of the current application by invoking
ApplicationDescriptor.currentApplicationDescriptor(). For example, to display the name of
the current application, you would write code as in the following example:
ApplicationDescriptor descriptor =
ApplicationDescriptor.currentApplicationDescriptor();
String appname = descriptor.getName();

144 BlackBerry Java Development Environment


Working with the application manager

Post global events


You can use the ApplicationManager.postGlobalEvent method as a basic mechanism to
communicate with other processes. You can also send and receive messages between processes
using the runtime store. Refer to "Communicating with other applications" on page 151 for more
information.
• To post a global event to a specific application, use the following method:

abstract boolean postGlobalEvent(int processId, long guid, int data0,


int data1, Object object0, Object object1)

The processID parameter specifies the ID of the process to which to post the event. You can
retrieve a process ID by invoking the getProcessId(ApplicationDescriptor) method. The guid
parameter specifies a global unique identifier (GUID) for the event. The data and object
parameters specify additional information for the event.
• To post a global event to all applications, use one of the following forms of the
postGlobalEvent() method:

/* Form 1 */
boolean postGlobalEvent(long guid);
/* Form 2 */
boolean postGlobalEvent(long guid, int data0, int data1);
/* Form 3 */
abstract boolean postGlobalEvent(long guid, int data0, int data1,
Object object0, Object object1);

The first form of the method posts a global event with a unique identifier. The second form of the
method posts a global event with additional data. The third form of the method posts a global event
with additional integer and object data.
To receive a global event, an application must implement the
net.rim.device.api.system.GlobalEventListener interface and implement the
GlobalEventListener.eventOccurred method, which is invoked when a global event occurs. In its
implementation of eventOccurred(), the application specifies which actions to perform when a
global event is received.
Register the GlobalEventListener by invoking the
Application.addGlobalEventListener(GlobalEventListener) method.

Developer Guide 145


Chapter 10: Managing applications

Lock the handheld


To determine whether the user’s handheld is locked, invoke
ApplicationManager.isSystemLocked(). This method returns true if the handheld is locked.

You can use the ApplicationManager.lockSystem method to lock the user’s handheld.
For example, you might want to lock the handheld if the user types a password for your application
incorrectly a certain number of times. The following sample code demonstrates how to do this:
ApplicationManager manager = ApplicationManager.getApplicationManager();
manager.lockSystem(true);

If the user has set a password, the lock screen appears and the user must type this password to use
the handheld again. If a password is not set, the keyboard lock screen appears and the user must
double-click the trackwheel to use the handheld again.

Run an application with different arguments


You can use the ApplicationManager and ApplicationDescriptor classes to run an application with
different arguments, for example, based on a system event.
1. Create an ApplicationDescriptor using the existing ApplicationDesriptor as a template.
Specify the arguments to use in the main() method.

ApplicationDescriptor template =
ApplicationDescriptor.currentApplicationDescriptor();
String[] args = { "admin", "secure" }
ApplicationDescriptor newdescriptor = new ApplicationDescriptor(
template, args);

The ApplicationDescriptor constructor has two other forms:

/* Form 2 */
ApplicationDescriptor(ApplicationDescriptor original, String name,
String[] args);
/* Form 3 */
ApplicationDescriptor(ApplicationDescriptor original, String name,
String[] args, Bitmap icon, int position, String nameResourceBundle,
int nameResourceId);

These forms of the constructor specify a name for the new ApplicationDescriptor. The third
form of the constructor also specifies initial settings, including an application icon, a Home
screen position, and the resource bundle and ID to use for the application title.

146 BlackBerry Java Development Environment


Working with the application manager

2. Run the application using the new ApplicationDescriptor.

ApplicationManager appmanager = ApplicationManager.getApplicationManager();


try {
appmanager.runApplication(newdescriptor);
} catch(ApplicationManagerException) {
//handle situation when application cannot run
}

The runApplication() method creates a new process and invokes the exported main() method in
the specified descriptor, using its arguments. The new process moves to the foreground if possible.

Schedule an application to run later


You can schedule the application to run at a specified time by invoking scheduleApplication()
instead of runApplication(), as demonstrated in the following code sample:
try {
appmanager.scheduleApplication(newdescriptor, 1728000, false);
} catch(ApplicationManagerException) {
//handle situation when application cannot run
}

The scheduleApplication() method requires three parameters:


• ApplicationDescriptor to use

• time at which to start the application, in milliseconds


• a Boolean value, where true indicates that the time is absolute (calculated from midnight,
January 1, 1970 UTC) and false indicates that the time is relative to midnight local time

Note: The application does not run if the handheld is reset or turned off before the specified time.

Developer Guide 147


Chapter 10: Managing applications

Working with code modules


The net.rim.device.api.system.CodeModuleManager class enables you to perform the following
tasks:
• retrieve information about code modules
• manage modules on the handheld
A code module is a .cod file—the compiled archive of a single project in the IDE. On the handheld
Home screen, click the Options icon and then click Applications to view a list of third-party
applications that are installed on the handheld. Click the Properties menu item to view information
about an application.

Retrieve module information


Methods of the CodeModuleManager class enable you to retrieve information about code modules on
the handheld, such as the name, type, description, version, and creation date.
1. Retrieve a handle for the module by invoking getModuleHandle(). In this method, specify the
name of the code module.

int handle = CodeModuleManager.getModuleHandle("test_module");

2. Invoke methods of the CodeModuleManager class to retrieve specific information, and specify the
module handle as a parameter. For example:

String name = CodeModuleManager.getModuleName( handle );


String vendor = CodeModuleManager.getModuleVendor( handle );
String description = CodeModuleManager.getModuleDescription( handle );
int version = CodeModuleManager.getModuleVersion( handle );
int size = CodeModuleManager.getModuleCodeSize( handle );
int timestamp = CodeModuleManager.getModuleTimestamp( handle );

Retrieve an array of handles


You can invoke getModuleHandles() to retrieve an array of handles for all existing modules on the
handheld.
int handles[] = CodeModuleManager.getModuleHandles();
String name = CodeModuleManager.getModuleName( handles[0]);

148 BlackBerry Java Development Environment


Managing code modules

Other methods of interest


The following methods provide additional capabilities for retrieving module information.
• Invoke getModuleHandleForObject() to retrieve the handle of the module in which an object’s
class is defined.

int handle = CodeModuleManager.getModuleHandleForObject( anObject );

• Invoke isLibrary() to determine whether a module is a library. This method returns true if the
module is a library or false if the module is an application.

boolean library = CodeModuleManager.isLibrary( handle );

• Invoke getModuleCodeSize() to determine the size, in bytes, of the code that a module contains.

int size = CodeModuleManager.getModuleCodeSize( handle );

• Invoke getApplicationDescriptors() to retrieve an array of all descriptors that a code module


contains.

ApplicationDescriptor descriptors[] =
CodeModuleManager.getApplicationDescriptors( handle )

Managing code modules


The net.rim.device.api.system.CodeModuleManager class provides methods for creating, saving,
and deleting code modules. These capabilities enable an application on the handheld to receive .cod
files wirelessly.

Create a module
1. Invoke the createNewModule() method to create a new module on the handheld.

int handle = CodeModuleManager.createNewModule( 3000 );

This example creates an empty module, to which 3000 bytes is allocated. This method returns
the module handle (or 0 if the module cannot be created). Alternatively, you can add data to the
module when you create it by invoking this form of the createNewModule method:

static int createNewModule(int totalLength, byte[] data, int length);

The totalLength parameter specifies the length in bytes of the entire module, the data
parameter specifies a byte array of data to add to the module, and the length parameter
specifies the number of bytes from the byte array to add to the start of the module.

Developer Guide 149


Chapter 10: Managing applications

2. Invoke writeNewModule() to write a byte array of data into the module. In the following
example, data is a byte array.

if( handle != 0 ) {
CodeModuleManager.writeNewModule( handle, data, 0, data.length );
}

Tip: A module must have the correct format for a .cod file. Typically, you would read in the contents of a .cod file
that is sent to the handheld wirelessly. You can write data into a code module in increments, as long as you know
the offset at which to add data.

The writeNewModule method returns true if the module is saved successfully, or false if the
module is not saved.
3. Save the module to the handheld database.

if( handle != 0 ) {
int result = CodeModuleManager.saveNewModule( handle );
}

The saveNewModule() method returns one of the result codes that are defined in the
CodeModuleManager class, such as CMM_OK if the module is saved successfully, or
CMM_MODULE_INVALID if the module could not be saved because it is invalid.

Delete a module
To delete a module from the handheld, invoke the deleteModule() method. Specify the handle of
the module to delete, and a Boolean value to specify whether to delete the module and any data it
contains or to delete the module only if does not have any associated data. If the module is in use, it
is deleted the next time that the handheld is reset.
The deleteModule() method returns true if the module is deleted successfully (or scheduled for
deletion) or false if the module is not deleted.
The following code sample demonstrates how to delete a module.
int handle = CodeModuleManager.getModuleHandle("test_module");
if( handle != 0 ) {
CodeModuleManager.deleteModule( handle, true );
}

150 BlackBerry Java Development Environment


Chapter 11
Communicating with
other applications
This section provides information on the following topics:
• Runtime store
• Adding objects to the runtime store
• Retrieving objects from the runtime store
Chapter 11: Communicating with other applications

Runtime store
The BlackBerry handheld uses a runtime store to provide a central location in which applications
can share runtime objects. By default, applications on the handheld that have been digitally signed
by RIM can access data in the runtime store. For information on how to control access to your data,
contact RIM.
Invoke RuntimeStore.getRuntimeStore() to retrieve the singleton RuntimeStore instance:
RuntimeStore store = RuntimeStore.getRuntimeStore();

You can then invoke methods on RuntimeStore to add or retrieve runtime objects.

Note: The runtime store is not persistent. If the handheld is reset, data in the runtime store is lost.

Adding runtime objects


You can add new objects to the runtime store or replace existing objects.

Add a runtime object


1. Retrieve the RuntimeStore object.

RuntimeStore store = RuntimeStore.getRuntimeStore();

2. Create an object.

String msg = "Some shared text";

3. Create a long ID to identify the object uniquely in the runtime store:

long ID = 0x60ac754bc0867248L;

You use the same key to retrieve the object from the runtime store.

152 BlackBerry Java Development Environment


Retrieving runtime objects

4. Invoke the RuntimeStore.put() method to add the object to the runtime store.

try {
store.put( ID, msg );
} catch(IllegalArgumentException e) {
//handle exception
}

When you invoke this method, you must specify a long ID and the Object to store.
The put() method throws an IllegalArgumentException if an object with the same ID already
exists in the runtime store. To replace an existing object, use the replace() method.

Replace a runtime object


To replace an object in the runtime store, use the replace() method:
RuntimeStore store = RuntimeStore.getRuntimeStore();
String newmsg = "Some new text";
Object obj = store.replace( 0x60ac754bc0867248L, newmsg);

The replace() method returns the existing object with that ID in the runtime store, or returns null if
no existing object exists with the specified ID.
The replace() method throws a ControlledAccessException if you do not have write permissions
on this object.

Retrieving runtime objects


You can retrieve a runtime object in one of two ways:
• RuntimeStore.get() retrieves an object that is already registered

• RuntimeStore.waitFor() waits for an object to be registered before retrieving it

Developer Guide 153


Chapter 11: Communicating with other applications

Retrieve an object
1. Invoke the RuntimeStore.getRuntimeStore() method.

RuntimeStore store = RuntimeStore.getRuntimeStore();

2. Invoke the RuntimeStore.get() method. Provide the object ID as a method parameter:

try {
Object obj = store.get(0x60ac754bc0867248L);
} catch(ControlledAccessException e) {
//handle exception
}

The get() method returns the object with the specified ID from the runtime store, or returns
null if no object exists with this ID.

Wait for an object


To wait for an object to be registered, invoke RuntimeStore.waitFor(), as shown in the following
example:
RuntimeStore store = RuntimeStore.getRuntimeStore();
try {
Object obj = store.waitFor(0x60ac754bc0867248L);
} catch(ControlledAccessException e) {
//handle exception - insufficient permissions
} catch(RuntimeException e) {
//handle exception - time out
}

Note: If the object with the specified ID does not exist, the waitFor() method blocks for a maximum of
MAX_WAIT_MILLIS. The method throws a RuntimeException if the object is not registered by this time.

154 BlackBerry Java Development Environment


Appendix A
Sample code
This section provides the following sample code:
• Base class for UI applications
• Persistence sample application
Appendix A: Sample code

Base class for UI applications


The following code provides an example of a base class that you can write to share and reuse
common functionality across applications. Most sample applications in this guide extend the
BaseApp class.

Example: BaseApp.java

/*
* BaseApp.java
* Copyright (C) 2001-2003 Research In Motion Limited. All rights reserved.
*/

package com.rim.docs.samples.baseapp;

import net.rim.device.api.i18n.*;
import net.rim.device.api.system.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;

public abstract class BaseApp extends UiApplication implements


BaseAppResource, KeyListener, TrackwheelListener
{
private MenuItem _closeItem;
private static ResourceBundle _resources =
ResourceBundle.getBundle(BUNDLE_ID, BUNDLE_NAME);
/* constructor for the abstract base class */
public BaseApp()
{
_closeItem = new MenuItem(_resources, MENUITEM_CLOSE, 200000, 10) {
public void run()
{
onExit();
System.exit(0);
}
};
}

156 BlackBerry Java Development Environment


Base class for UI applications

/* override this method to add custom menu items */


protected void makeMenu( Menu menu, int instance)
{
Field focus =UiApplication.getUiApplication().
getActiveScreen().getLeafFieldWithFocus();
if(focus != null) {
ContextMenu contextMenu = focus.getContextMenu();
if( !contextMenu.isEmpty()) {
menu.add(contextMenu);
menu.addSeparator();
}
}
menu.add(_closeItem);
}
/* invoked when the trackwheel is clicked */
public boolean trackwheelClick( int status, int time )
{

Menu menu = new Menu();


makeMenu( menu, 0);
menu.show();
return true;
}

/* invoked when the trackwheel is released */


public boolean trackwheelUnclick( int status, int time )
{
return false;
}

/* invoked when the trackwheel is rolled */


public boolean trackwheelRoll(int amount, int status, int time) {
return false;
}
public boolean keyChar(char key, int status, int time) {
//intercept the ESC key - exit the app on its receipt
boolean retval = false;

switch (key) {
case Characters.ESCAPE:
onExit();
System.exit(0);
retval = true;
break;
}
return retval;
}

Developer Guide 157


Appendix A: Sample code

/* implementation of KeyListener.keyDown */
public boolean keyDown(int keycode, int time) {
return false;
}
/* implementation of KeyListener.keyRepeat */
public boolean keyRepeat(int keycode, int time) {
return false;
}
/* implementation of KeyListener.keyStatus */
public boolean keyStatus(int keycode, int time) {
return false;
}
/* implementation of KeyListener.keyUp */
public boolean keyUp(int keycode, int time) {
return false;
}

protected abstract void onExit();


}

Persistence sample application


The following source code provides a complete sample application for adding and editing
information about your favorite restaurants:
• MoreRestaurants.java (refer to page 159)

• MenuHelper.java (refer to page 168)

• CookieMenuItem.java (refer to page 171)

Refer to "Storing persistent data" on page 79 for more information on persistence.


The main class for this application, MoreRestaurants, extends BaseApp, which implements common
UI functionality. Refer to "Base class for UI applications" on page 156 for more information.
This example also requires that you create resource files in the application project and define the
required resource keys. Refer to "Localizing applications" on page 123 for more information.

158 BlackBerry Java Development Environment


Persistence sample application

Example: MoreRestaurants.java

/*
* MoreRestaurants.java
* Copyright (C) 2001-2003 Research In Motion Limited. All rights reserved.
*/

package com.rim.docs.samples.restaurants;

import java.util.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.system.*;
import net.rim.device.api.util.*;
import net.rim.device.api.i18n.*;
import com.rim.docs.samples.baseapp.*;

public class MoreRestaurants extends BaseApp implements


MoreRestaurantResource, KeyListener, TrackwheelListener {

private MenuItem _closeItem;


private ListField list;
private int display;

private static PersistentObject _store;


private static DbList _db;
private static ResourceBundle _resources;

public static final int FOR_EDIT = 100;


public static final int FOR_READ = 110;
public static final int FOR_LIST = 120;
public static final int FOR_TITLE = 130;

static {
_resources = ResourceBundle.getBundle(
MoreRestaurantResource.BUNDLE_ID,
MoreRestaurantResource.BUNDLE_NAME);
_store = PersistentStore.getPersistentObject(
0xa077a0437e4d00c0L );
synchronized( _store ) {
if( _store.getContents() == null ) {
_store.setContents( new Vector() );
_store.commit();

Developer Guide 159


Appendix A: Sample code

}
}
_db = new DbList();
_db.set((Vector)_store.getContents());
}

public static void main(String[] args) {

MoreRestaurants app = new MoreRestaurants();


app.enterEventDispatcher();
}

//inner classes
/**
* <p>A holder for a vector of objects
* <p>This class acts as a container for the persistent objects
* and renders the list on screen.
*/
private static class DbList implements ListFieldCallback
{
private Vector _listOfItems;
private int _width;
private Controller _controller;

public DbList()
{
_controller = new Controller();
}

public void set(Vector objectList)


{
_listOfItems = objectList;
}

public void setWidth(int width)


{
_width = width;
}

public int getWidth()


{
return _width;
}

public int getNumItems()


{
return _listOfItems.size();
}

160 BlackBerry Java Development Environment


Persistence sample application

public Restaurant get(int index)


{
return (Restaurant)_listOfItems.elementAt(index);
}

//implement ListFieldCallback methods


public void drawListRow(ListField listField, Graphics graphics,
int index, int y, int width)
{
//called by the list field when a row requires painting
if ( index >= _listOfItems.size() )
{
//do not do anything
return;
}

//load the controller with the appropriate item


_controller.load((Restaurant)_listOfItems.elementAt(index));

//draw a list representation


graphics.drawText((String)_controller.render(FOR_LIST), 0, y,
0, width);
}

public Object get(ListField listField, int index)


{
//retrieves the Object representation for the requested element.
return (Object)_listOfItems.elementAt(index);
}

public int getPreferredWidth(ListField listField)


{
//called by the list field
return Graphics.getScreenWidth();
}

public int indexOfList(ListField listField, String prefix,


int start)
{
//called by the list field to find the first element that
//starts with a given prefix.
return 0; //not implemented
}

Developer Guide 161


Appendix A: Sample code

/* commit a new restaurant to the persistent store */


public void commit(Restaurant info)
{
if ( !_listOfItems.contains(info) )
{
_listOfItems.addElement(info);
}
PersistentObject.commit(_listOfItems);
}
}

/* Restaurant class - creates a persistable object */


private static final class Restaurant implements Persistable
{
//data
private Vector _elements;

//fields
public static final int NAME = 0;
public static final int ADDRESS = 1;
public static final int PHONE = 2;
public static final int SPECIALTY = 3;

public Restaurant()
{
_elements = new Vector(4); //initial capacity of 4
for ( int i = 0; i < _elements.capacity(); ++i)
{
_elements.addElement(new String(""));
}
}

public String getElement(int id)


{
return (String)_elements.elementAt(id);
}

public void setElement(int id, String value)


{
_elements.setElementAt(value, id);
}
}

162 BlackBerry Java Development Environment


Persistence sample application

/* Controller class for rendering restaurant models */


private static class Controller {

//data
private AutoTextEditField name;
private AutoTextEditField address;
private EditField phone;
private EditField specialty;
private Restaurant _theModel;

public Controller()
{
this(null);
}

public Controller(Restaurant info)


{
name = new AutoTextEditField(
_resources.getString(FIELD_RESTAURANTNAME), "");
address = new AutoTextEditField(
_resources.getString(FIELD_ADDRESS), "");
phone = new EditField(
_resources.getString(FIELD_PHONENUMBER), "",
Integer.MAX_VALUE, BasicEditField.FILTER_PHONE);
specialty = new EditField(
_resources.getString(FIELD_SPECIALTY), "",
Integer.MAX_VALUE, BasicEditField.FILTER_DEFAULT);
load(info);
}

public boolean load(Restaurant info)


{
if ( null == info )
{
//create a new info
_theModel = new Restaurant();
}
else
{
_theModel = info;
//load the fields from the model
name.setText(info.getElement(Restaurant.NAME));
address.setText(info.getElement(Restaurant.ADDRESS));
phone.setText(info.getElement(Restaurant.PHONE));
specialty.setText(info.getElement(Restaurant.SPECIALTY));
}
return true;
}

Developer Guide 163


Appendix A: Sample code

public Object render(int context)


{
Object o = null;
switch (context)
{
case FOR_EDIT:
{
Object[] array =
{name, address, phone, specialty};
o = new ObjectEnumerator(array);
}
break;
case FOR_LIST:
o = new String(_theModel.getElement(Restaurant.NAME));
break;
}
return o;
}

/* commit the object to the device's persistent store */


public void save()
{
//store the data from the fields
_theModel.setElement(Restaurant.NAME, name.getText());
_theModel.setElement(Restaurant.ADDRESS, address.getText());
_theModel.setElement(Restaurant.PHONE, phone.getText());
_theModel.setElement(
Restaurant.SPECIALTY, specialty.getText());
_db.commit(_theModel);
}
}

//Add menu item


private class AddMenu extends MenuItem
{
private Restaurant _restaurantinfo;

public AddMenu()
{
super(_resources, MENUITEM_ADD, 1, 1);

//create the new model


_restaurantinfo = new Restaurant();
}

public void run()


{

164 BlackBerry Java Development Environment


Persistence sample application

final Controller controller = new Controller(_restaurantinfo);

//create a new screen for adding a restaurant


final MainScreen addScreen = new MainScreen();
addScreen.setTitle(new LabelField(
_resources.getString(ADDSCREEN_TITLE)));

//retrieve the various fields for edit


for ( Enumeration e =
(Enumeration)controller.render(FOR_EDIT);
e.hasMoreElements() ;)
{
addScreen.add((Field)e.nextElement());
}

MenuItem[] menuItemArray = new MenuItem[2];

MenuItem saveItem = new CookieMenuItem(


_resources, MENUITEM_SAVE, 110, 10, controller) {
public void run() {
((Controller)_cookie).save();
list.setSize(_db.getNumItems());
popScreen(addScreen);
}
};

menuItemArray[0] = saveItem;

MenuItem cancelItem = new CookieMenuItem(


_resources, MENUITEM_CANCEL, 110, 10, controller) {
public void run() {
if ( Dialog.SAVE == Dialog.ask(Dialog.D_SAVE) )
{
((Controller)_cookie).save();
list.setSize(_db.getNumItems());
}
popScreen(addScreen);
}
};
menuItemArray[1] = cancelItem;
MenuHelper mh = new MenuHelper(menuItemArray, 0);

addScreen.addKeyListener(mh);
addScreen.addTrackwheelListener(mh);
pushScreen(addScreen);
}
}

Developer Guide 165


Appendix A: Sample code

//Edit menu item


private class EditMenu extends MenuItem
{
private Restaurant _restaurantinfo;

public EditMenu(Restaurant info)


{
super(_resources, MENUITEM_EDIT, 1, 1);
_restaurantinfo = info; //load the existing model
}

public void run()


{
Controller controller = new Controller(_restaurantinfo);

//create a new screen for edit


final MainScreen editScreen = new MainScreen();
editScreen.setTitle(new LabelField(
_resources.getString(EDITSCREEN_TITLE)));
//get the various fields for edit
for ( Enumeration e =
(Enumeration)controller.render(FOR_EDIT);
e.hasMoreElements() ;)
{
editScreen.add((Field)e.nextElement());
}

MenuItem[] menuItemArray = new MenuItem[2];


MenuItem saveItem = new CookieMenuItem(
_resources, MENUITEM_SAVE, 110, 10, controller) {
public void run() {
((Controller)_cookie).save();
list.setSize(_db.getNumItems());
popScreen(editScreen);
}
};
menuItemArray[0] = saveItem;

MenuItem cancelItem = new CookieMenuItem(


_resources, MENUITEM_CANCEL, 110, 10, controller) {
public void run() {
if ( Dialog.SAVE == Dialog.ask(Dialog.D_SAVE) )
{
((Controller)_cookie).save();
list.setSize(_db.getNumItems());
}
popScreen(editScreen);
}
};

166 BlackBerry Java Development Environment


Persistence sample application

menuItemArray[1] = cancelItem;

MenuHelper mh = new MenuHelper(menuItemArray, 0);

editScreen.addKeyListener(mh);
editScreen.addTrackwheelListener(mh);
pushScreen(editScreen);
}
}

public MoreRestaurants()
{

MainScreen mainScreen = new MainScreen();


mainScreen.setTitle(new LabelField(
_resources.getString(APPLICATION_TITLE)));

mainScreen.addKeyListener(this);
mainScreen.addTrackwheelListener(this);

//add the list of restaurants


list = new ListField(_db.getNumItems());
list.setCallback(_db);
mainScreen.add(list);

pushScreen(mainScreen);
}

public void makeMenu( Menu menu ) {


AddMenu addItem = new AddMenu();
menu.add(addItem);
if (_db.getNumItems() > 0)
{
EditMenu editItem = new EditMenu(
_db.get(list.getSelectedIndex()));
menu.add(editItem);
}
super.makeMenu(menu);
}

public void onExit()


{
Dialog.alert(_resources.getString(APP_EXIT));
}
}

Developer Guide 167


Appendix A: Sample code

Example: MenuHelper.java

/**
* MenuHelper.java
* Copyright (C) 2001-2003 Research In Motion Limited. All rights reserved.
*/

package com.rim.docs.samples.restaurants;

import java.util.*;
import net.rim.device.api.system.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.i18n.*;
import net.rim.device.api.util.*;
import com.rim.docs.samples.baseapp.*;

public class MenuHelper implements KeyListener, TrackwheelListener


{
private MenuItem[] _menuItems;
private int _defaultIndex;

//statics
private static MenuItem _close;
private static ResourceBundle _resources;
static {
//Resources for the project
_resources = ResourceBundle.getBundle(
MoreRestaurantResource.BUNDLE_ID,
MoreRestaurantResource.BUNDLE_NAME);
_close = new MenuItem(_resources,
BaseAppResource.MENUITEM_CLOSE, 200000, 10) {
public void run() {
UiApplication uiapp = UiApplication.getUiApplication();
uiapp.popScreen(uiapp.getActiveScreen());
return;
}
};
}
public MenuHelper()
{
this(null, 0);
}

168 BlackBerry Java Development Environment


Persistence sample application

public MenuHelper(MenuItem[] menuItems, int defaultIndex)


{
setMenuItems(menuItems, defaultIndex);
}
public void setMenuItems(MenuItem[] menuItems, int defaultIndex)
{
if ( null == menuItems )
{
_menuItems[0] = _close;
_defaultIndex = 0;
}
else
{
_close = null;
_menuItems = menuItems;
_defaultIndex = defaultIndex;
}
}
protected void makeMenu( Menu menu )
{
Field focus = UiApplication.getUiApplication().
getActiveScreen().getLeafFieldWithFocus();
int nItems = 0;
if(focus != null) {
ContextMenu contextMenu = focus.getContextMenu();
if(!contextMenu.isEmpty()) {
menu.add(contextMenu);
menu.addSeparator();
}
}
//add the menu items passed in by the constructor
for (int i = 0; i < _menuItems.length; ++i )
{
menu.add(_menuItems[i]);
}

menu.setDefault(_defaultIndex + nItems);
}
public boolean trackwheelClick( int status, int time )
{
Menu menu = new Menu();

makeMenu( menu );

menu.show();
menuSelected( menu, menu.getSelectedCookie(), menu.getSelectedId() );
return true;

Developer Guide 169


Appendix A: Sample code

}
public boolean trackwheelUnclick( int status, int time )
{
return false;
}
public boolean trackwheelRoll(int amount, int status, int time) {
return false;
}
public void menuSelected( Menu menu, Object cookie, int id )
{
}
public boolean keyChar(char key, int status, int time) {
//intercept the ESC key - exit the application on its receipt
boolean retval = false;

switch (key) {
case Characters.ESCAPE:
UiApplication uiapp = UiApplication.getUiApplication();
uiapp.popScreen(uiapp.getActiveScreen());
retval = true;
break;
}
return retval;
}
public boolean keyDown(int keycode, int time) {
return false;
}
public boolean keyRepeat(int keycode, int time) {
return false;
}
public boolean keyStatus(int keycode, int time) {
return false;
}
public boolean keyUp(int keycode, int time) {
return false;
}
}

170 BlackBerry Java Development Environment


Persistence sample application

Example: CookieMenuItem.java

/**
* CookieMenuItem
* Copyright (C) 2001-2003 Research In Motion Limited. All rights reserved.
*/
package com.rim.docs.samples.restaurants;

import net.rim.device.api.system.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.i18n.*;
import net.rim.device.api.util.*;
import java.util.*;

public abstract class CookieMenuItem extends MenuItem


{
protected Object _cookie;

public CookieMenuItem(ResourceBundle bundle, int id, int ordinal,


int priority, Object cookie) {
super(bundle, id, ordinal, priority);
_cookie = cookie;
}
}

Developer Guide 171


Appendix A: Sample code

172 BlackBerry Java Development Environment


Appendix B
Format of .alx files
This section provides information on the following topics:
• About .alx files
• Elements in .alx files
Appendix B: Format of .alx files

About .alx files


The Application Loader tool, which is part of the BlackBerry Desktop Software, uses an application
loader (.alx) file to load new applications onto the handheld. Use the IDE to generate .alx files for
your projects. Refer to "Create application loader files" on page 31 for more information.
The following information is provided only as a supplementary reference. In most cases, you do not
need to edit the .alx files generated in the IDE.

Example file
In a text editor, you can edit .alx files that the IDE generates. The .alx file uses an XML format. This is
an example of a .alx file for one application:
<loader version="1.0">
<application id="com.rim.samples.device.httpdemo">
<name>Sample Network Application</name>
<description>Retrieves a sample page over HTTP connection.
</description>
<version>1.0</version>
<vendor>Research In Motion</vendor>
<copyright>Copyright 1998-2003 Research In Motion</copyright>
<language langid="0x000c">
<name>Application D'Échantillon</name>
<description>Obtenir une page du réseau
</description>
</language>
<fileset Java="1.0">
<directory>samples/httpdemo</directory>
<files>
net_rim_httpdemo.cod
net_rim_resource.cod
net_rim_resource__en.cod
net_rim_resource__fr.cod
</files>
</fileset>
</application>
</loader>

174 BlackBerry Java Development Environment


About .alx files

Nesting modules
You can create a nested structure in an .alx file to provide optional components for an application.
Typically, nested modules provide optional features that are not applicable to all users. Users can
choose whether to install optional modules.
Nesting creates an implicit dependency for nested modules on the base application. To define an
explicit dependency on another application or library, use the <requires> tag. Refer to "Elements in
.alx files" on page 176 for more information.
The following is an example of a .alx file for an application with a nested module:
<loader version="1.0">
<application id="net.rim.sample.contacts">
<name>Sample Contacts Application</name>
<description>Provides the ability to store a list of contacts.
</description>
<version>1.0</version>
<vendor>Research In Motion</vendor>
<copyright>Copyright 1998-2001 Research In Motion</copyright>
<fileset Java="1.0">
<directory>samples/contacts</directory>
<files>
net_rim_contacts.cod
net_rim_resource.cod
net_rim_resource__en.cod
net_rim_resource__fr.cod
</files>
</fileset>
<application id="net.rim.sample.contacts.mail">
<name>Sample Module for Contacts E-Mail Integration</name>
<description>Provides the ability to access the email
applicaton</description>
<version>1.0</version>
<vendor>Research In Motion</vendor>
<copyright>Copyright 1998-2001 Research In Motion</copyright>
<fileset Java="1.0">
<directory>samples/contacts</directory>
<files>
net_rim_contacts_mail.cod
</files>
</fileset>
</application>
</application>
</loader>

Developer Guide 175


Appendix B: Format of .alx files

Elements in .alx files


The following table describes each element (tag) and attribute. Elements are mandatory unless
noted otherwise.

Element Attributes Description


loader version The loader element contains one or more application elements.
The version attribute specifies the version of the Application Loader. The version in
this release is 1.0.
application id The application element contains the elements for a single application.
The application element can also contain additional nested application
elements. Nesting enables you to require that when an application is loaded, its
prerequisite modules are also loaded.
The id attribute specifies a unique identifier for the application. You should use an ID
that includes your company domain, in reverse, to provide uniqueness (for example,
com.rim.samples.device.helloworld).

library id The library element can be used instead of the application tab. It contains the
elements for a single library module. No nested modules are permitted. By default, a
library module is hidden so that it does not appear in Application Loader.
Typically, you would use the library element as the target of a <requires> element, so
that when a particular application is loaded onto the handheld, a required library is
also loaded.
Note: this element is supported by BlackBerry Desktop Software version 3.6 or later.
name — The name element provides a descriptive name for the application, which appears in
the Application Loader. It does not appear on the handheld.
description — The description element provides a brief description of the application. The
description appears in the Application Loader. It does not appear on the handheld.
version — The version element provides the version number of the application, which appears
in the Application Loader. This version number is for information purposes only.
vendor — The vendor element provides the name of the company that created the application,
which appears in the Application Loader.
copyright — The copyright element provides copyright information, which appears in the
Application Loader.
required — The required element enables you to force an application to be loaded. In the
Application Loader, the application is selected to install, and the user cannot change
this. Add the following line: <required>true</required>
The required tag should be used by corporate system administrators only; it is not
intended for use by third-party software vendors.
Note: this element is supported by BlackBerry Desktop Software version 3.5 or later.

176 BlackBerry Java Development Environment


Elements in .alx files

Element Attributes Description


hidden — The hidden element hides a package so that it does not appear to users in the
Application Loader. Add the following line: <hidden>true</hidden>.
Typically, you would use this element in conjunction with the required element to
load the application by default, or you would set the requires tag to load this
package if another application is loaded.
The hidden tag should be used by corporate system administrators only; it is not
intended for use by third-party software vendors.
Note: this element is supported by BlackBerry Desktop Software version 3.6 or later.
language langid The language tag enables you to override the text that appears in the Application
Loader when the Application Loader is running in the language specified by the
langid attribute.
You can specify multiple language tags, one for each language that you want to
support. In each language tab, you can nest the name, description, version,
vendor, and copyright tags. If you do not nest a tag, the text appears in the default
language.
The langid attribute specifies the Win32 langid code for the language to which this
information applies. For example, some Win32 langid codes are: 0x0009 (English),
0x0007 (German), 0x000a (Spanish), 0x000c (French).

requires id The requires element is an optional element that specifies the id of a package on
which this application depends. This element can appear more than once, if the
application depends on more than one other application.
When an application is loaded onto the use’s handheld, all packages that are
specified by the <requires> tag are also loaded.
Note: this element is supported by BlackBerry Desktop Software version 3.6 or later.

Developer Guide 177


Appendix B: Format of .alx files

Element Attributes Description


fileset Java The fileset element includes an optional directory element and one or more
radio files elements. It specifies a set of .cod files, in a single directory, to load onto the
langid handheld. If you need to load files from more than one directory, you can include one
or more fileset elements in the .alx file.
The Java attribute specifies the minimum version of the BlackBerry Java VM with
which the .cod files are compatible. The current VM is version 1.0. The Java attribute
is required.
The radio attribute enables you to load different applications or modules depending
on the network type of the handheld. Possible values are: Mobitex, DataTAC, GPRS,
CDMA, and IDEN. The radio attribute is optional.
The langid attribute enables you to load different applications or modules
depending on the language support that users add to the handheld. The value is a
Win32 langid code; for example: 0x0009 (English), 0x0007 (German), 0x000a
(Spanish), 0x000c (French). The langid attribute is optional.
directory — The directory element provides the location of a set of files. The directory
element is optional. If you do not specify a directory, the files must be in the same
location as the .alx file.
You can specify a directory element for an application or for each fileset. The
directory is specified relative to the location of the .alx file.
files — The files element provides a list of one or more .cod files, in a single directory, to
load onto the handheld for an application.

178 BlackBerry Java Development Environment


Acronym list
API application programming interface

APN access point name

ALX Application Loader XML file

CA Certificate Authority

cHTML Compact Hypertext Markup Language

CDMA code division multiple access

CHAP Challenge Handshake Authentication Protocol

CLDC Connected Limited Device Configuration

COD code file

CPU central processing unit

CRM customer relationship management

DES Data Encryption Standard

DNS Domain Name Server

FAQ frequently asked question

GIF Graphics Interchange Format

GPRS General Packet Radio Service

GUI graphical user interface

GUID globally unique identifier

JCA Java Cryptography Architecture

J2ME Java 2 Platform, Micro Edition


Chapter 14: Acronym list

J2SE Java 2 Platform, Standard Edition

JDE Java development environment

JDK Java development kit

JDP Java development project

JDW Java development workspace

HTML Hypertext Markup Language

HTTP Hypertext Transfer Protocol

HTTPS Hypertext Transfer Protocol Secure

i18n internationalization

ID identification

IDE integrated development environment

iDEN Integrated Digital Enhanced Network

IMEI International Mobile Equipment Identity

IMSI International Mobile Subscriber Identity

I/O input/output

IP Internet Protocol

IPPP IP Proxy Protocol

ISDN Integrated Services Digital Network

ISO International Organization for Standardization

JAD Java descriptor

JAR Java archive

JPEG Joint Photographic Experts Group

JRE Java runtime environment

KB Kilobyte

KVM KB virtual machine

180 BlackBerry Java Development Environment


LAN local area network

LCD liquid crystal display

LDAP Lightweight Directory Access Protocol

LED light-emitting diode

LTPA Lightweight Third Party Authentication

MB megabyte

MDS Mobile Data Service

MHz megahertz

MIDlet MIDP application

MIDP Mobile Information Device Profile

MIME Multipurpose Internet Mail Extensions

MSISDN Mobile Station ISDN

OCSP Online Certificate Status Protocol

PAP Password Authentication Protocol

PDA personal digital assistant

PIM personal information management

PIN personal identification number

PNG portable network graphics

PRM partner relationship management

RAM random access memory

RRC resource content

RRH resource header

RTC real-time clock

SDK software development kit

SIM Subscriber Identity Module

Developer Guide 181


Chapter 14: Acronym list

SMS short message service

SRAM static random access memory

SRP Service Relay Protocol

SSL Secure Sockets Layer

TCP Transmission Control Protocol

TCP/IP Transmission Control Protocol/Internet Protocol

TIFF tag image file format

TLS Transport Layer Security

UDP User Datagram Protocol

UI user interface

URI Uniform Resource Identifier

URL Uniform Resource Locator

UTC Universal Time Coordinate

VM virtual machine

WAP Wireless Application Protocol

WBMP Wireless Bitmap

WML Wireless Markup Language

WMLC WML Compiled

WTLS Wireless Transport Layer Security

XHTML Extensible HTML

XML Extensible Markup Language

182 BlackBerry Java Development Environment


Index

Index
A backing up data, 114
deploying, 40, 69
address book
inter-process communication, 145, 151
about, 45
managing, 144
converting to serial formats, 49
retrieving information about, 144
creating a contact, 45
running, 146
example, 51
scheduling, 147
importing contacts, 50
system modules, 106
opening ContactList, 45
See also code modules
removing a contact, 48
appointments, See calendar
retrieving contact information, 48
attachments
saving a contact, 48
about, 35
See also PIM
registering a handler, 36
alternate entry points, 106
retrieving contents, 36
.alx files
retrieving information, 36
examples, 174
sending, 37
format, 176
writing to an output stream, 37
using, 40, 69
auto-run applications, 106
APIs
access control, 14 B
application, 15
code signing, 14 backing up data, 104
messaging, 23 backup and restore, See synchronization
networking, 131
notification, 115
C
options, 71 calendar
persistent storage, 79 adding appointment information, 61
PIM, 41 converting to serial formats, 64
runtime, 14 creating a recurring appointment, 62
SMS, 135 creating an appointment, 61
synchronization, 97 opening a list, 61
application APIs, 15 retrieving appointment information, 63
application descriptor, 146 saving an appointment, 63
application loader, 40, 69 See also PIM
application manager, 144 CLDC, 11
application registry, See runtime store .cod files, 19
applications code modules
auto-run on startup, 106 creating, 149

Developer Guide 183


Index

deleting, 150 examples


methods, 149 BaseApp.java, 156
retrieving handles, 148 BasicMail.java, 33
retrieving information, 148 ConsequenceDemo.java, 129
code signing ContactsDemo.java, 51
about, 14 DemoOptionsProvider.java, 76
registering, 16 MoreRestaurants.java, 159
requesting signatures, 19 NotificationsDemo.java, 123
Signature Tool, 19 ReceiveSms.java, 140
verification, 16 Restaurants.java, 92
CodeModuleManager, 148 RestaurantsSync.java, 107
connections, UDP, 131 SendSms.java, 138
Consequence, 126 TaskDemo.java, 58
ContactList UserInfo.java, 86
closing, 48
opening, 45 F
contacts, See Address Book files
ContactsDemo.java, 51 .alx files, 176
creating .cod files, 19
initialization project, 106 .csl files, 19
synchronization collections, 101 .cso files, 19
synchronization objects, 100 FolderEvent, 26
.csl files, 19 folders
.cso files, 19 listing, 32
managing, 32
D saving a message, 33
datagrams, See UDP searching, 32
deploying applications
messaging API, 40 G
PIM APIs, 69 global events, 145
desktop software, connecting to the simulator,
114 H
E handheld
locking, 146
email server simulator, using, 38
email, See messaging I
enumerateRecords() method, 81
inter-process communication
ESS, See email server simulator
global events, 145
events
runtime store, 152
calendar, 61
IPPP, 131
global, 145
messaging, 26

184 BlackBerry Java Development Environment


Index

J O
J2ME, 11 options
about, 72
L adding an item, 73
library projects, 118 implementing public methods, 76
locking the handheld, 146 registering, 72
storing data, 75
M
P
managing applications, 144
MessageEvent, 26 persistence
messaging about, 80
about, 24 creating a database, 84
attachments, 35 custom objects, 88
creating messages, 24 example, 86, 92, 158
events, 26 MIDP API, 80
managing events, 26 persistable objects, 89
managing folders, 32 retrieving an object, 91
multipart messages, 25 retrieving data, 85
reading messages, 28 sample, 158
receiving messages, 26 saving an object, 90
replying to a message, 30 saving data, 84
saving a message, 33 storage space, 82
services, 24 PIM
storing messages, 25 about, 42
fields, 43
N items, 43
lists, 42
networking
See also Address Book, Calendar, Tasks
SMS connections, 135
UDP connections, 131 R
notification
canceling events, 120 receiving
consequences, 126 data on UDP connection, 134
custom system notifications, 126 email messages, 26
deferred events, 119 SMS messages, 139
events, 117 record stores, 80
example, 123 references
immediate events, 116, 119 BlackBerry web site, 11
listener, 121 CLDC, 11
profile configurations, 116 J2ME, 11
responding to events, 121 MIDP, 11
triggering events, 119 registering
NotificationsEngineListener, 121 attachment handler, 36

Developer Guide 185


Index

notification event source, 117 sending a message, 137


options items, 72 testing, 141
synchronization collection, 106 storing data, See persistence, 80
restoring data, 105 SyncCollection, enabling, 106
retrieving SyncConverter, 126
attachment contents, 36 synchronization
attachment information, 36 backing up data, 104
runtime creating synchronization collections, 101
APIs, 14 creating synchronization objects, 100
storage, 14 initializing, 106
runtime store registering a collection, 106
about, 152 restoring data, 105
adding objects, 152 testing, 114
replacing objects, 153
retrieving objects, 154 T
TaskDemo.java, 58
S tasks
saving data, See persistence, 80 adding task information, 54
scheduling applications to run, 147 converting to serial formats, 56
security creating a task, 54
handheld lock, 146 example, 58
See also code signing opening a list, 54
sending removing a task, 56
attachments, 37 retrieving task information, 56
datagrams, 133 See also PIM
messages, 29 testing
SMS messages, 137 backup and restore, 114
serial formats messaging applications, 38
appointments, 64 SMS application, 141
contacts, 49 to do, See tasks
tasks, 56
Signature Tool, 19 U
signing, See code signing UDP
simulator about, 132
connecting to the desktop software, 114 opening a connection, 133
email server, 38 receiving data, 134
using null modem cable, 114 sending data, 133
SMS
about, 136 W
example, 138, 140
WAP, 131
opening a connection, 136
receiving a message, 139

186 BlackBerry Java Development Environment


© 2002-2003 Research In Motion Limited
Published in Canada

You might also like