Applications can register for notifications that are sent when certain system-level events or application-level events occur. Notifications are similar to application launch codes, with one important difference: notifications are only sent to applications or code resources that have specifically registered to receive them, making them more efficient than launch codes.
This chapter describes the Palm OS notification mechanism. It shows how to register for a notification and how to deal with the notifications that you then receive. It provides some detail on some of the more commonly-used notifications: those that signal when the device is about to go to sleep and those that indicate that it is waking up. This chapter then discusses a special class of notifications—helper notifications—that can be used to publish and request application services. Finally, it concludes with a complete list of all of the notifications defined by Palm OS.
Notification Overview
Registering for a Notification
Writing a Notification Handler
Sleep and Wake Notifications
Helper Notifications
Notification Summary
Notification Function Summary
Reference material for many of the APIs discussed in this chapter can be found in Chapter 4, "Notifications."
Notification Overview
The Palm OS system and the built-in applications send notifications when certain events occur. (For a complete list, see "Notification Summary".) It's also possible for your application to create and broadcast its own notifications. However, applications rarely do so. It's more likely that you'll want to register to receive the predefined notifications or that you'll broadcast the predefined sysNotifyHelperEvent
described under "Helper Notifications."
A given notification is sent to each of the notification clients that register for it. Three general types of event flow are possible using the notification manager:
- Single consumer
Each client is notified that the event has occurred and handles it in its own way without modifying any information in the parameter block.
- Collaborative
The notification's parameter block contains a
handled
flag. Clients can set this flag to communicate to other clients that the event has been handled, while still allowing them to receive the notification. - Collective
Each client can add information to the notification's parameter block, allowing the data to be accumulated for all clients. This style of notification could be used, for example, to build a menu dynamically by letting each client add its own menu text. The
sysNotifyMenuCmdBarOpenEvent
is similar to this style of notification.
Registering for a Notification
To receive notification that an event has occurred, you must register for it using the SysNotifyRegister()
function. Once you register for a notification, you remain registered until the system is reset, the notification is deleted, or until you explicitly unregister for this notification using SysNotifyUnregister()
.
To register an application for the HotSync® notification, you'd use a function call similar to the one in Listing 4.1.
Listing 4.1 Registering for a notification
SysNotifyRegister(appDBID, sysNotifySyncStartEvent, NULL, sysNotifyNormalPriority, myDataP, myDataSize);
The parameters you pass to the SysNotifyRegister()
function specify the following:
- The database ID for the PRC file. Be sure you're not passing the local ID of the record database that your application accesses. You use the record database's local ID more frequently than you do the application's local ID, so this is a common mistake to make.
- The notification for which you are registering. In the above examples,
sysNotifySyncStartEvent
specifies that you want to be informed when a HotSync operation is about to start. (There is also asysNotifySyncFinishEvent
that specifies that a HotSync operation has ended.) - The means by which the notification should be received. Applications should use
NULL
for this parameter to specify that they should be notified through the application launch codesysAppLaunchCmdNotify
. As with all other launch codes, the system passes this to the application'sPilotMain()
function. - The priority with which the notification should be sent.
sysNotifyNormalPriority
means that you don't want your code to receive any special consideration when receiving the notification. Notifications are broadcast synchronously in priority order. The lower the number you specify here, the earlier you receive the notification in the list.In virtually all cases, you should use
sysNotifyNormalPriority
. If you absolutely must ensure that your code is notified in a certain order (either before most notifications or after most notifications), be sure to leave some space between priority values so that your code won't collide with the system's handling of notifications or with another application's handling of notifications. Never use the extreme maximum or minimum allowed value. In general, PalmSource recommends using a value whose least significant bits are 0 (such as 32, 64, 96, and so on). - Any data you want easy access to in your notification handler function.
After you've made the calls shown in Listing 4.1 and the system is about to begin a HotSync operation, it broadcasts the sysNotifySyncStartEvent
notification to both clients. Along with the notification your code receives a SysNotifyParamType
structure containing the notification name, the broadcaster, and a pointer to your specific data (myDataP
in the example above). Some notifications contain extra information in a notifyDetailsP
field in this structure. (The HotSync notifications do not use the notifyDetailsP
field.)
Writing a Notification Handler
The application's (or a library's) response to sysAppLaunchCmdNotify
is called a notification handler. A notification handler may perform any processing necessary, including displaying a user interface or broadcasting other notifications.
When displaying a user interface, consider the possibility that you may be blocking other applications from receiving the notification. For this reason, it's generally not a good idea to display a modal form or do anything else that requires waiting for the user to respond. Also, many of the notifications are broadcast during SysHandleEvent()
, which means your application event loop may not have progressed to the point where it is possible for you to display a user interface, or that you may overflow the stack.
If you need to perform some lengthy process in a notification handler, one way to ensure that you aren't blocking other events is to send yourself a deferred notification. For example, Listing 4.2 shows a notification handler for the sysNotifyTimeChangeEvent
notification that performs no work other than setting up a deferred notification (myDeferredNotifyEvent
--which is a custom notification) and scheduling it for broadcast. When the application receives the myDeferredNotifyEvent
, it calls the MyNotifyHandler function, which is where the application really handles the time change event.
Listing 4.2 Deferring notification within a handler
case sysAppLaunchCmdNotify :
if (cmdPBP->notify->notifyType == sysNotifyTimeChangeEvent
) {
SysNotifyParamType notifyParm;
MyNotificationDataStruct myData;
/* initialize myData here */
/* Create the notification block. */
notifyParam.notifyType = myDeferredNotifyEvent;
notifyParam.broadcaster = myCreatorID;
notifyParam.notifyDetailsP= NULL;
notifyParam.handled = false;
/* Register for my notification */
SysNotifyRegister(myCardNo, appDBID, myDeferredNotifyEvent, NULL,
sysNotifyNormalPriority, &myData);
/* Broadcast the notification */
SysNotifyBroadcastDeferred(¬ifyParam, NULL);
} else if (cmdPBP->notify->notifyType == myDeferredNotifyEvent)
MyNotifyHandler(cmdPBP->notify);
break;
The SysNotifyBroadcastDeferred()
function broadcasts the specified notification to all interested parties; however, it waits to do so until the current event has completed processing. Thus, by using a separate deferred notification, you can be sure that all other clients have had a chance to respond to the first notification.
There are several functions that broadcast notifications. Notification handlers should use SysNotifyBroadcastDeferred()
to avoid the possibility of overflowing the notification stack.
A special case of dealing with lengthy computations in a notification handler occurs when the system is being put to sleep. See "Sleep and Wake Notifications" below.
Sleep and Wake Notifications
Several notifications are broadcast at various stages when the system goes to sleep and when the system wakes up. These are:
-
sysNotifySleepRequestEvent
- Broadcast during
SysHandleEvent()
processing when the system has decided to go to sleep. -
sysNotifySleepNotifyEvent
- Broadcast during
SysHandleEvent()
immediately before the system is put to sleep. After the broadcast is complete, the system is put to sleep. -
sysNotifyEarlyWakeupEvent
- Broadcast during
SysHandleEvent()
immediately after the system has finished sleeping. The screen may still be turned off, and the system may not fully wake up. It may simply handle an alarm or a battery charger event and go back to sleep. -
sysNotifyLateWakeupEvent
- Broadcast during
SysHandleEvent()
immediately after the device has finished waking up.
These notifications are not guaranteed to be broadcast. For example, if the system goes to sleep because the user removes the batteries, sleep notifications are not sent. Thus, these notifications are unsuitable for applications where external hardware must be shut off to conserve power before the system goes to sleep.
If you want to know when the system is going to sleep because you have a small amount of cleanup that should occur beforehand, then register for sysNotifySleepNotifyEvent
.
It is recommended that you not perform any sort of prolonged activity, such as displaying an alert panel that requests confirmation, in response to a sleep notification. If you do, the alert might be displayed long enough to trigger another auto-off event, which could be detrimental to other handlers of the sleep notify event.
In a few instances, you might need to prevent the system from going to sleep. For example, your code might be in the middle of performing some lengthy computation or in the middle of attempting a network connection. If so, register for the sysNotifySleepRequestEvent
instead. This notification informs all clients that the system might go to sleep. If necessary, your handler can delay the sleep request by doing the following:
((SleepEventParamType *) (notify->notifyDetailsP))->deferSleep++;
The system checks the deferSleep
value when each notification handler returns. If it is nonzero, it cancels the sleep event.
After you defer sleep, your code is free to finish what it was doing. When it is finished, you must allow the system to continue with the sleep event. To do so, create a keyDownEvent
with the resumeSleepChr
and the command key bit set (to signal that the character is virtual) and add it to the event queue. When the system receives this event, it will again broadcast the sysNotifySleepRequestEvent
to all clients. If deferSleep
is 0 after all clients return, then the system knows it is safe to go to sleep, and it broadcasts the sysNotifySleepNotifyEvent
to all of its clients.
Notice that you may potentially receive the sysNotifySleepRequestEvent
many times before the system actually goes to sleep, but you receive the sysNotifySleepNotifyEvent
exactly once.
During a wake-up event, the other two notifications listed above are broadcast. The sysNotifyEarlyWakeupEvent
is broadcast very early on in the wake-up process, generally before the screen has turned on. At this stage, it is not guaranteed that the system will fully wake up. It may simply handle an alarm or a battery charger event and go back to sleep. Most applications that need notification of a wake-up event will probably want to register for sysNotifyLateWakeupEvent
instead. At this stage, the screen has been turned on and the system is guaranteed to fully wake up.
When the handheld receives the sysNotifyLateWakeupEvent
notification, it may be locked and waiting for the user to enter the password. If this is the case, you must wait for the user to unlock the handheld before you display a user interface. Therefore, if you intend to display a user interface when the handheld wakes up, you should make sure the handheld is not locked. If the handheld is locked, you should register for sysNotifyDeviceUnlocked
notification and display your user interface when it is received. See Listing 4.3.
Listing 4.3 Responding to Late Wake-up Notification
case sysNotifyLateWakeupEvent: if ((Boolean) PrefGetPreference(prefDeviceLocked)) { SysNotifyRegister(myDbID, sysNotifyDeviceUnlocked, NULL, sysNotifyNormalPriority, NULL); } else { HandleDeviceWakeup(); } case sysNotifyDeviceUnlocked: HandleDeviceWakeup();
Helper Notifications
The helper notification, sysNotifyHelperEvent
, is a way for one application to request a service from another application. Currently, the Dial application is the only application that performs a service through sysNotifyHelperEvent
. Specifically, the Dial application dials a phone in response to this notification. The Address Book uses the Dial application to dial the phone number that the user has selected. You can use the Dial application in a similar way by broadcasting the sysNotifyHelperEvent
from your application. You may also choose to write a provider of services.
In this section, the application that responds to the sysNotifyHelperEvent
notification is called the helper, and the application that broadcasts the notification is called the broadcaster.
A helper registers for the sysNotifyHelperEvent
notification. In the notification handler, the helper responds to action requests pertaining to the service that it provides.
Actions are requests to provide information about the service or to perform the service. The details structure for sysNotifyHelperEvent
(a HelperNotifyEventType
structure) defines three possible actions:
-
kHelperNotifyActionCodeEnumerate
is a request for the helper to list the services that it can perform. -
kHelperNotifyActionCodeValidate
is a request for the helper to make sure that it can perform the service. -
kHelperNotifyActionCodeExecute
is a request to actually perform the service.
The possible services are defined in HelperServiceClass.h
and described in Chapter 10, "Helper Service Class." These services are to dial a number, email a message, send an SMS message, or send a fax. If you want to define your own service, you must register a unique creator ID for that service. Alternatively, you can use the creator ID of your application.
When to Use the Helper API
There are several means by which one application can communicate with another application on the same handheld. Specifically, an application can send a launch code to another application (see "Launching Applications Programmatically", can use the Exchange Manager and Local Exchange Library to send data to another application (see Chapter 4, "Object Exchange," in Exploring Palm OS: High-Level Communications), or can use the helper API to request that a service be performed.
The helper API is designed for use when you do not know anything about the receiving application. The helper API provides a means of communication where the sending and receiving application do not need to know anything about each other. This contrasts with the launch code mechanism, in which the sending application must know the local ID of the receiving database as well as which launch code to send.
Requesting a Helper Service
Listing 4.4 shows how an application should request the dial service. In general, you should do the following to request a service:
- Broadcast a
sysNotifyHelperEvent
with akHelperNotifyActionCodeValidate
action each time you want to verify that the service is available.For example, when the Address Book initializes the List view form, it checks to see if the dial service is available by broadcasting the notification with the action code
kHelperNotifyActionCodeValidate
. The Dial application makes sure the Telephony Library is open. If so, it setshandled
totrue
in theSysNotifyParamType
structure. If not, it setshandled
tofalse
. Ifhandled
isfalse
after the notification is broadcast, the Address Book does not display the Dial menu item. - Broadcast a
sysNotifyHelperEvent
with akHelperNotifyActionCodeExecute
action when you want the service performed. See Listing 4.4. - If you want to obtain a list of all possible services, broadcast a
sysNotifyHelperEvent
with akHelperNotifyActionCodeEnumerate
action. You might do so when your application is launched, upon system reset, or any time the user performs a task where you might want to provide a service.
Listing 4.4 Requesting a helper service
Boolean PrvDialListDialSelected(FormType* frmP) { SysNotifyParamType param; HelperNotifyEventType details; HelperNotifyExecuteType execute; param.notifyType = sysNotifyHelperEvent; param.broadcaster = sysFileCAddress; param.notifyDetailsP = &details; param.handled = false; details.version = kHelperNotifyCurrentVersion; details.actionCode = kHelperNotifyActionCodeExecute; details.data.executeP = &execute; execute.serviceClassID = kHelperServiceClassIDVoiceDial; execute.helperAppID = 0; execute.dataP = FldGetTextPtr(ToolsGetFrmObjectPtr(frmP, DialListNumberField)); execute.displayedName = gDisplayName; execute.detailsP = 0; execute.err = errNone; SysNotifyBroadcast(¶m); // Check error code if (!param.handled) // Not handled so exit the list - Unexpected error return true; else return (execute.err == errNone); }
When you broadcast the sysNotifyHelperEvent
, it's important to note the following:
- Always use
SysNotifyBroadcast()
, which broadcasts the notification synchronously. - The notification's
notifyDetailsP
parameter points to aHelperNotifyEventType
. This structure allows the broadcaster to communicate with the helper. - The helper may allocate memory and add it to the
HelperNotifyEventType
structure. In particular, if the action code iskHelperNotifyActionCodeEnumerate
, the helper allocates at least one structure of typeHelperNotifyEnumerateListType
and adds it to thedata
field in theHelperNotifyEventType
structure. The broadcaster must free this memory, even though the helper allocated it. - The broadcaster uses the
helperAppID
field to communicate directly with a particular provider of the requested service. For example, suppose two applications provide a dial service. The broadcaster might discover these two applications through the enumerate action and then allow the user to specify which application should dial the phone number. When broadcasting the enumerate action, no helper ID is specified, so all helpers respond. After the user has set the preferred helper, the broadcaster sets thehelperAppID
field for the validate and execute actions to that helper's creator ID. A helper must check thehelperAppID
field and only respond to the notification if its creator ID matches the value in that field or if that field is 0. - The
dataP
field contains the data required to perform the service. For the dial service,dataP
contains the phone number to dial. If any extra information is required or desired, then it is provided in thedetailsP
field. If you're requesting the email or SMS service, you usedetailsP
to provide the message to be sent. See Chapter 10, "Helper Service Class," for more information. - The
handled
field ofSysNotifyParamType
and theerr
field of theHelperNotifyEventType
structure are used to return the result. Always sethandled
tofalse
anderr
toerrNone
before broadcasting and check their values after the broadcast is complete. The helper useshandled
to indicate if it attempted to handle the service. Ifhandled
istrue
, it useserr
to indicate the success or failure of performing that service.
Implementing a Helper
To implement a helper, do the following:
- Register to receive the
sysNotifyHelperEvent
. It is best to register for this notification in response to thesysAppLaunchCmdSyncNotify
andsysAppLaunchCmdSystemReset
launch codes. This registers your helper when it is first installed and re-registers it upon each system reset. - In the notification handler, handle the three possible actions: enumerate, execute, and validate. Note that even though the enumerate action is optional and not currently used by Address Book, a helper must respond to this action in its handler because another third party application might send the enumerate action.
Listing 4.5 and Listing 4.6 show how the Dial application responds to the enumerate and validate actions. Note that the enumerate action requires the helper to allocate memory and add that memory to the HelperNotifyEventType
structure pointed to by notifyDetailsP
in the SysNotifyParamType
parameter block. In this case, the notifyDetailsP->dataP
field is a linked list of HelperNotifyEnumerateListType
structures. Each helper must allocate one of these structure per service and add it to the end of the list. The broadcaster is responsible for freeing all of these structures after the notification broadcast is complete.
Listing 4.5 Enumerating services provided
Boolean PrvAppEnumerate (HelperNotifyEventType *helperNotifyEventP) { HelperNotifyEnumerateListType* newNodeP; MemHandle handle; MemPtr stringP; newNodeP = MemPtrNew (sizeof(HelperNotifyEnumerateListType)); // Get name to display in user interface. handle = DmGetResource(strRsc, HelperAppNameString); stringP = MemHandleLock(handle); StrCopy(newNodeP->helperAppName, stringP); MemHandleUnlock(handle); DmReleaseResource(handle); // Get name of service to display in UI. handle = DmGetResource(strRsc, HelperActionNameString); stringP = MemHandleLock(handle); StrCopy(newNodeP->actionName, stringP); MemHandleUnlock(handle); DmReleaseResource(handle); newNodeP->serviceClassID = kHelperServiceClassIDVoiceDial; newNodeP->helperAppID = kDialCreator; newNodeP->nextP = 0; // Add the new node. if (helperNotifyEventP->data.enumerateP == 0) { helperNotifyEventP->data.enumerateP = newNodeP; else { HelperNotifyEnumerateListType* nodeP; nodeP = helperNotifyEventP->data.enumerateP; //Look for the end of the list. while ( nodeP->nextP != 0 ) nodeP = nodeP->nextP; nodeP->nextP = newNodeP; } return true; }
Listing 4.6 show how the Dial application responds to the validate action.
Listing 4.6 Responding to validate action
Boolean PrvAppValidate (SysNotifyParamType *sysNotifyParamP) { HelperNotifyEventType* helperNotifyEvent; helperNotifyEvent = sysNotifyParamP->notifyDetailsP; // Check version if (helperNotifyEvent->version < 1) return false; // Check service if (helperNotifyEvent-> data.validateP->serviceClassID != kHelperServiceClassIDVoiceDial) return false; // check appId (either null or me) if ((helperNotifyEvent->data.validateP->helperAppID != 0) && (helperNotifyEvent->data.validateP->helperAppID != kDialCreator)) return false; // Check Telephony library presence if (!PrvAppCheckTelephony()) return false; sysNotifyParamP->handled = true; return true; }
When writing a helper, it is also important to note the following:
- Always check the
helperAppID
field and only respond if it is 0 or if it matches your creator ID. For the validate and execute actions, a broadcaster may usehelperAppID
to only communicate with the desired helper. - If you handle the action, set
handled
totrue
. If the handling of the service was unsuccessful, set theerr
field innotifyDetailsP
. - Always check the
handled
field before performing the service. If any helper can perform the service, you must make sure that the service has not already been performed before you perform it. Ifhandled
istrue
, the service has already been performed.
Notification Summary
Table 4.1 lists the standard notifications that are supported in Palm OS Cobalt. These notifications are declared in the header NotifyMgr.h
. All the parameters for a notification are passed in a SysNotifyParamType
structure and the results are returned in that same structure.
Table 4.1 Notification Constants
Broadcast by the Connection Manager whenever a persistent profile is either connected or disconnected. |
|
An alternative input system (such as an external keyboard) has become disabled. |
|
An alternative input system (such as an external keyboard) has been enabled. |
|
An expansion card has been inserted into the expansion slot. |
|
Database info has been set on a database, such as with |
|
An overlay has been opened, a database has been opened for write, or another event has occurred which has made the database info "dirty." |
|
The user has tapped the Lost Password button in the Security application. |
|
An application has requested that a particular service be performed. |
|
The Attention Manager has failed to post a virtual character to the key queue. |
|
Notification Function Summary
|
|