Professional Documents
Culture Documents
Environment
Version 3.6
Developer Guide
© 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.
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
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
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.
Conventions
Warning: Warnings advise you that failure to take or avoid a specific action could result in data loss or physical
damage.
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
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
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.
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
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.
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
1. In the BlackBerry JDE bin folder, double-click SignatureTool.jar. The Signature Tool window
appears.
2. Click Change Password. A dialog box appears.
Developer Guide 19
Chapter 1: Using controlled APIs
5. Click Open. The selected .cod files are added to the list.
When the files are signed, the Status column for the .cod file displays Signed.
Developer Guide 21
Chapter 1: Using controlled APIs
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.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.
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
• 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.
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.
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
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.
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");
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.
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.
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.
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);
Developer Guide 29
Chapter 2: Integrating email
4. Add CC or BCC recipients in the same way that you added TO recipients.
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());
}
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();
Transport.send(reply);
Forward a message
1. Invoke forward() on an existing Message. The forward() method returns a new Message object.
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.
try {
Transport.send(fwdmsg);
} catch(MessagingException e) {
System.out.println(e.getMessage());
}
Developer Guide 31
Chapter 2: Integrating email
List folders
You can list the folders in a mailbox store by invoking the Store.list() method.
Folder[] folders = store.list();
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 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.
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.*;
BasicMail() {
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);
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.
2. Implement the menuString() method to return the menu item string to display on the Messages
screen when the user selects an attachment.
3. Implement the run() method to perform the appropriate processing on the attachment data and
display it to the user.
Note: The run() method is invoked when the corresponding menu item is selected on the Messages screen.
Developer Guide 35
Chapter 2: Integrating email
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.
AttachmentHandlerManager m = AttachmentHandlerManager.getInstance();
Send an attachment
1. Create a Message object.
The default MultiPart constructor creates a MultiPart object with a type of multipart/mixed.
4. Create an attachment part by invoking the SupportedAttachmentPart constructor.
mp.addBodyPart(attach);
Developer Guide 37
Chapter 2: Integrating email
msg.setContent(mp);
Transport.send(msg);
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>
...
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.
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.
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
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));
}
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().
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
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);
}
//add address
contact.addStringArray(Contact.ADDR, Contact.ATTR_NONE, address);
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.";
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);
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.
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);
}
}
}
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);
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();
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.
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.*;
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));
//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;
}
else
{
try
{
ContactList contactList = (ContactList)PIM.getInstance().
openPIMList(PIM.CONTACT_LIST, PIM.WRITE_ONLY);
Contact contact = contactList.createContact();
//reset UI fields
_first.setText("");
_last.setText("");
_email.setText("");
_phone.setText("");
_pin.setText("");
return true;
}
catch (PIMException e) {
return false;
}
}
}
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.
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.
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);
}
For example, the following code sets a task status to “in progress”:
task.addInt(ToDo.EXTENDED_FIELD_MIN_VALUE + 9, ToDo.ATTR_NONE, 2);
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.
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);
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);
}
}
}
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();
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.*;
private TaskDemo()
{
_taskScreen = new TaskScreen();
pushScreen(_taskScreen);
}
public TaskScreen()
{
_saveMenuItem = new SaveMenuItem();
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());
_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;
}
}
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.
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
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.
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.
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);
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();
}
Enumeration e = eventList.items();
while (e.hasMoreElements()) {
Developer Guide 63
Chapter 3: Integrating PIM functions
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);
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.
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();
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 EventScreen()
{
_saveMenuItem = new SaveMenuItem();
setTitle(new LabelField("Event Demo", LabelField.ELLIPSIS |
LabelField.USE_ALL_WIDTH) );
add(_location);
add(new SeparatorField());
add(_startTime);
add(_endTime);
add(new SeparatorField());
if(_repeat.getSelectedIndex() != 0) {
event.setRepeat(setRule());
}
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() {
<application id="sampleapp">
<requires id="net.rim.blackberry.api" />
<name>Sample application</name>
...
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
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.
Implement OptionsProvider
Your application must implement the OptionsProvider interface and register as an options provider
to add option items.
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.
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.
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 .
3. Implement the save() method to save changes to any fields on the screen.
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.
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.
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()
{
}
Developer Guide 75
Chapter 4: Adding options
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();
}
private OptionsDemoData()
{
}
Developer Guide 77
Chapter 4: Adding options
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();
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
Tip: Users can view the current amount of available data storage in the handheld Options application, on the
Status screen.
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.
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.
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();
}
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);
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.*;
static {
_resources = ResourceBundle.getBundle(
UserInfoResource.BUNDLE_ID, UserInfoResource.BUNDLE_NAME);
store = PersistentStore.getPersistentObject(0xa1a569278238dad2L);
}
Dialog.inform(_resources.getString(APP_SUCCESS));
usernamefield.setText(null);
passwordfield.setText(null);
}
};
public UserInfo()
{
MainScreen mainScreen = new MainScreen();
mainScreen.setTitle(new LabelField(
_resources.getString(APPLICATION_TITLE)));
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));
}
}
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();
}
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
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
_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.
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.*;
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("");
}
};
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();
}
Developer Guide 93
Chapter 5: Storing persistent data
//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 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);
mainScreen.add(namefield);
mainScreen.add(addressfield);
mainScreen.add(phonefield);
mainScreen.add(specialtyfield);
mainScreen.addKeyListener(this);
mainScreen.addTrackwheelListener(this);
pushScreen(mainScreen);
}
Developer Guide 95
Chapter 5: Storing 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.
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.
Interface Description
SyncConverter converts data between the SyncObject format required on the handheld and a serialized format
required on the desktop
SyncObject represents an object that can be backed up and restored to the user’s computer
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
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.
2. Define a _uid variable and implement getUID() to return a unique ID to use for synchronization
operations.
3. Define a constructor that accepts a unique as a parameter and sets the _uid variable to this
value.
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.
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.
4. Define a variable and method to retrieve a new instance of the RestaurantsSync class.
7. Implement the two versions of getSyncName() to provide a descriptive name for the
synchronization collection.
12. Implement removeAllSyncObjects() to remove all synchronization objects from the collection.
This method can be invoked when data is restored from the desktop.
13. Implement addSyncObject() to add a synchronization object to the collection. This method can
be called when data is restored.
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.
Implement SyncConverter
1. Add tags for each field in your persistent object.
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.
3. Implement convert(DataBuffer data, int version, int UID). This method is called when
data is restored to the handheld from the desktop.
if (startup) {
//enable application for synchronization on startup
SerialSyncManager.getInstance().enableSynchronization(new
RestaurantsSync());
} else {
RestaurantsSync app = new RestaurantsSync();
app.enterEventDispatcher();
}
}
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.*;
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();
}
if (startup) {
//enable application for synchronization on startup
SerialSyncManager.getInstance().enableSynchronization(
RestaurantsSync.getInstance());
} else {
RestaurantsSync app = new RestaurantsSync();
app.enterEventDispatcher();
}
}
public RestaurantInfo()
{
_elements = new Vector(4); //initial capacity of 4
for ( int i = 0; i < _elements.capacity(); ++i)
{
_elements.addElement(new String(""));
}
}
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;
}
return false;
}
return null;
}
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);
mainScreen.add(namefield);
mainScreen.add(addressfield);
mainScreen.add(phonefield);
mainScreen.add(specialtyfield);
mainScreen.addKeyListener(this);
mainScreen.addTrackwheelListener(this);
pushScreen(mainScreen);
}
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.
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
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.
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
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.
Parameter Description
sourceID identifier of the application that starts the event (as specified when you invoked
registerSource())
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.
Parameter Description
sourceID identifier of the application that starts the event (as specified when you invoked
registerSource())
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)
context optional object that can store additional, arbitrary parameters to control the state or behavior of
an event notification
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.
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.
2. Provide a constructor for your listener class. In the following example, declare a UiApplication
object to use to display a dialog box.
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.
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 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);
}
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.
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.
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'};
Alert.startAudio(TUNE, VOLUME);
Alert.startBuzzer(TUNE, VOLUME);
}
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.
} catch (EOFException e) {
System.err.println(e);
}
return null;
}
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.
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.
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.*;
Alert.startAudio(TUNE, VOLUME);
Alert.startBuzzer(TUNE, VOLUME);
}
Alert.stopAudio();
Alert.stopBuzzer();
}
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.
Note: Using UDP connections requires that you work closely with service providers. Contact your
service provider to verify that UDP connections are supported.
host destination host address to which to send data, in dotted decimal format, such as —
112.11.11.11
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) —
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.
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.
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());
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).
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.
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.setAddress("sms://+15555551234");
_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.
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.*;
//members
private String addr = "15191112222";
private String msg = "This is a test message.";
private DatagramConnection _dc = null;
private static String _openString = "sms://";
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);
}
}
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);
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.
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.*;
ReceiveSms() {
_listener = new ListeningThread();
_listener.start();
}
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();
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.
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.
ApplicationDescriptor template =
ApplicationDescriptor.currentApplicationDescriptor();
String[] args = { "admin", "secure" }
ApplicationDescriptor newdescriptor = new ApplicationDescriptor(
template, args);
/* 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.
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.
Note: The application does not run if the handheld is reset or turned off before the specified time.
2. Invoke methods of the CodeModuleManager class to retrieve specific information, and specify the
module handle as a parameter. For example:
• 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.
• Invoke getModuleCodeSize() to determine the size, in bytes, of the code that a module contains.
ApplicationDescriptor descriptors[] =
CodeModuleManager.getApplicationDescriptors( handle )
Create a module
1. Invoke the createNewModule() method to create a new module on the handheld.
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:
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.
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 );
}
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.
2. Create an object.
long ID = 0x60ac754bc0867248L;
You use the same key to retrieve the object from the runtime store.
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.
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.
Retrieve an object
1. Invoke the RuntimeStore.getRuntimeStore() method.
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.
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.
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.*;
switch (key) {
case Characters.ESCAPE:
onExit();
System.exit(0);
retval = true;
break;
}
return retval;
}
/* 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;
}
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.*;
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();
}
}
_db = new DbList();
_db.set((Vector)_store.getContents());
}
//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();
}
//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(""));
}
}
//data
private AutoTextEditField name;
private AutoTextEditField address;
private EditField phone;
private EditField specialty;
private Restaurant _theModel;
public Controller()
{
this(null);
}
public AddMenu()
{
super(_resources, MENUITEM_ADD, 1, 1);
menuItemArray[0] = saveItem;
addScreen.addKeyListener(mh);
addScreen.addTrackwheelListener(mh);
pushScreen(addScreen);
}
}
menuItemArray[1] = cancelItem;
editScreen.addKeyListener(mh);
editScreen.addTrackwheelListener(mh);
pushScreen(editScreen);
}
}
public MoreRestaurants()
{
mainScreen.addKeyListener(this);
mainScreen.addTrackwheelListener(this);
pushScreen(mainScreen);
}
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.*;
//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);
}
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;
}
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;
}
}
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.*;
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>
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>
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.
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.
CA Certificate Authority
i18n internationalization
ID identification
I/O input/output
IP Internet Protocol
KB Kilobyte
MB megabyte
MHz megahertz
UI user interface
VM virtual machine
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
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