On desktop computers, an application starts up when a user launches it and stops when the user chooses the Exit or Quit command. These things occur a little bit differently on the Palm OS® handheld. A Palm OS application does launch when the user requests it, but it may also launch in response to some other user action, such as a request for the global find facility. Palm OS applications don't have an Exit command; instead they exit when a user requests another application.
This chapter describes how an application launches, how an application stops, and the code you must write to perform these tasks properly. It also covers notifications, which is another way for the system to launch your code when certain events occur. Notifications are available in later releases of the Palm OS. This chapter covers:
- Launch Codes and Launching an Application
- Responding to Launch Codes
- Launching Applications Programmatically
- Creating Your Own Launch Codes
- Stopping an Application
- Notifications
- Helper Notifications
- Launch Code Summary
- Notification Summary
- Launch and Notification Function Summary
This chapter does not cover the main application event loop. The event loop is covered in Chapter 3, "Event Loop."
Launch Codes and Launching an Application
An application launches when it receives a launch code. Launch codes are a means of communication between the Palm OS and the application (or between two applications).
For example, an application typically launches when a user presses one of the buttons on the device or selects an application icon from the application launcher screen. When this happens, the system generates the launch code sysAppLaunchCmdNormalLaunch
, which tells the application to perform a full launch and display its user interface.
Other launch codes specify that the application should perform some action but not necessarily become the current application (the application the user sees). A good example of this is the launch code used by the global find facility. The global find facility allows users to search all databases for a certain record, such as a name. In this case, it would be very wasteful to do a full launch—including the user interface—of each application only to access the application's databases in search of that item. Using a launch code avoids this overhead.
Each launch code may be accompanied by two types of information:
- A parameter block, a pointer to a structure that contains several parameters. These parameters contain information necessary to handle the associated launch code.
- Launch flags indicate how the application should behave. For example, a flag could be used to specify whether the application should display UI or not. (See "Launch Flags" in the Palm OS Programmer's API Reference.)
A complete list of all launch codes is provided at the end of this chapter in the section "Launch Code Summary." That section contains links into where each launch code is described in the Palm OS Programmer's API Reference.
Responding to Launch Codes
Your application should respond to launch codes in a function named PilotMain
. PilotMain
is the entry point for all applications.
When an application receives a launch code, it must first check whether it can handle this particular code. For example, only applications that have text data should respond to a launch code requesting a string search. If an application can't handle a launch code, it exits without failure. Otherwise, it performs the action immediately and returns.
Listing 2.1 shows parts of PilotMain
from the Datebook application as an example. To see the complete example, go to the examples folder in the Palm OS SDK and look at the file Datebook.c
.
Listing 2.1 PilotMain in Datebook.c
UInt32 PilotMain (UInt16 cmd, void *cmdPBP, UInt16 launchFlags) { return DBPilotMain(cmd, cmdPBP, launchFlags); } static UInt32 DBPilotMain (UInt16 cmd, void *cmdPBP, UInt16 launchFlags) { UInt16 error; Boolean launched; // This app makes use of PalmOS 2.0 features.It will crash // if run on an earlier version of PalmOS. Detect and warn // if this happens, then exit. error = RomVersionCompatible (version20, launchFlags); if (error) return error; // Launch code sent by the launcher or the datebook // button. if (cmd == sysAppLaunchCmdNormalLaunch) { error = StartApplication (); if (error) return (error); FrmGotoForm (DayView); EventLoop (); StopApplication (); } // Launch code sent by text search. else if (cmd == sysAppLaunchCmdFind) { Search ((FindParamsPtr)cmdPBP); } // This launch code might be sent to the app when it's // already running if the user hits the "Go To" button in // the Find Results dialog box. else if (cmd == sysAppLaunchCmdGoTo) { launched = launchFlags & sysAppLaunchFlagNewGlobals; if (launched) { error = StartApplication (); if (error) return (error); GoToItem ((GoToParamsPtr) cmdPBP, launched); EventLoop (); StopApplication (); else GoToItem ((GoToParamsPtr) cmdPBP, launched); } // Launch code sent by sync application to notify the // datebook application that its database has been synced. // ... // Launch code sent by Alarm Manager to notify the // datebook application that an alarm has triggered. // ... // Launch code sent by Alarm Manager to notify the // datebook application that is should display its alarm // dialog. // ... // Launch code sent when the system time is changed. // ... // Launch code sent after the system is reset. We use this // time to create our default database if this is a hard // reset // ... // Launch code sent by the DesktopLink server when it // creates a new database. We will initialize the new // database. return (0); }
Responding to Normal Launch
When an application receives the launch code sysAppLaunchCmdNormalLaunch
, it begins with a startup routine, then goes into an event loop, and finally exits with a stop routine. (The event loop is described in Chapter 3, "Event Loop." The stop routine is shown in the section "Stopping an Application" at the end of this chapter.)
During the startup routine, your application should perform these actions:
- Get system-wide preferences (for example for numeric or date and time formats) and use them to initialize global variables that will be referenced throughout the application.
- Find the application database by creator type. If none exists, create it and initialize it.
- Get application-specific preferences and initialize related global variables.
- Initialize any other global variables.
As you saw in Listing 2.1, the Datebook application example responds to sysAppLaunchCmdNormalLaunch
by calling a function named StartApplication
. Listing 2.2 shows the StartApplication
function.
Listing 2.2 StartApplication from Datebook.c
static UInt16 StartApplication (void) { UInt16 error = 0; Err err = 0; UInt16 mode; DateTimeType dateTime; DatebookPreferenceType prefs; SystemPreferencesType sysPrefs; UInt16 prefsSize; // Step 1: Get system-wide preferences. PrefGetPreferences (&sysPrefs); // Determime if secret records should be // displayed. HideSecretRecords = sysPrefs.hideSecretRecords; if (HideSecretRecords) mode = dmModeReadWrite; else mode = dmModeReadWrite | dmModeShowSecret; // Get the time formats from the system preferences. TimeFormat = sysPrefs.timeFormat; // Get the date formats from the system preferences. LongDateFormat = sysPrefs.longDateFormat; ShortDateFormat = sysPrefs.dateFormat; // Get the starting day of the week from the system // preferences. StartDayOfWeek = sysPrefs.weekStartDay; // Get today's date. TimSecondsToDateTime (TimGetSeconds(), &dateTime); Date.year = dateTime.year - firstYear; Date.month = dateTime.month; Date.day = dateTime.day; // Step 2. Find the application's data file. If it // doesn't exist, create it. ApptDB = DmOpenDatabaseByTypeCreator(datebookDBType, sysFileCDatebook, mode); if (! ApptDB) { error = DmCreateDatabase (0, datebookDBName, sysFileCDatebook, datebookDBType, false); if (error) return error; ApptDB = DmOpenDatabaseByTypeCreator(datebookDBType, sysFileCDatebook, mode); if (! ApptDB) return (1); error = ApptAppInfoInit (ApptDB); if (error) return error; } // Step 3. Get application-specific preferences. // Read the preferences/saved-state information. There is // only one version of the DateBook preferences so don't // worry about multiple versions. prefsSize = sizeof (DatebookPreferenceType); if (PrefGetAppPreferences (sysFileCDatebook, datebookPrefID, &prefs, &prefsSize, true) != noPreferenceFound) { DayStartHour = prefs.dayStartHour; DayEndHour = prefs.dayEndHour; AlarmPreset = prefs.alarmPreset; NoteFont = prefs.noteFont; SaveBackup = prefs.saveBackup; ShowTimeBars = prefs.showTimeBars; CompressDayView = prefs.compressDayView; ShowTimedAppts = prefs.showTimedAppts; ShowUntimedAppts = prefs.showUntimedAppts; ShowDailyRepeatingAppts = prefs.showDailyRepeatingAppts; } // Step 4. Initialize any other global variables. TopVisibleAppt = 0; CurrentRecord = noRecordSelected; // Load the far call jump table. FarCalls.apptGetAppointments = ApptGetAppointments; FarCalls.apptGetRecord = ApptGetRecord; FarCalls.apptFindFirst = ApptFindFirst; FarCalls.apptNextRepeat = ApptNextRepeat; FarCalls.apptNewRecord = ApptNewRecord; FarCalls.moveEvent = MoveEvent; return (error); }
Responding to Other Launch Codes
If an application receives a launch code other than sysAppLaunchCmdNormalLaunch
, it decides if it should respond to that launch code. If it responds to the launch code, it does so by implementing a launch code handler, which is invoked from its PilotMain
function.
In most cases, when you respond to other launch codes, you are not able to access global variables. Global variables are generally only allocated after an application receives sysAppLaunchCmdNormalLaunch
(see Listing 2.2) or sysAppLaunchCmdGoTo
; so if the application hasn't received either of these launch codes, its global variables are usually not allocated and not accessible. In addition, if the application has multiple code segments, you cannot access code outside of segment 0 (the first segment) if the application has no access to global variables.
There is one other case where an application may have access to its global variables (and to code segments other than 0). This is when an application is launched with the code sysAppLaunchCmdURLParams
. If this launch code results from a palm
URL, then globals are available. If the launch code results from a palmcall
URL, then globals are not available. The URL is passed to your application in the launch parameter block.
NOTE: Static local variables are stored with the global variables on the system's dynamic heap. They are not accessible if global variables are not accessible.
Checking launch codes is generally a good way to determine if your application has access to global variables. However, it actually depends on the setting of the launch flags that are sent with the launch code. In particular, if the sysAppLaunchFlagNewGlobals
flag is set, then your application's global variables have been allocated on this launch. This flag is set by the system and isn't (and shouldn't be) set by the sender of a launch code.
Boolean appHasGlobals = launchFlags & sysAppLaunchFlagNewGlobals
;
There's one case where this flag won't be set and your application will still have access to global variables. This is when your application is already running as the current application. In this case, its global variables have already been allocated through a previous launch.
If your application receives a launch code other than sysAppLaunchCmdNormalLaunch
or sysAppLaunchCmdGoTo
, you can find out if it is the current application by checking the launch flags that are sent with the launch code. If the application is the currently running application, the sysAppLaunchFlagSubCall
flag is set. This flag is set by the system and isn't (and shouldn't be) set by the sender of a launch code.
Boolean appIsActive = launchFlags & sysAppLaunchFlagSubCall
;
Launching Applications Programmatically
Applications can send launch codes to each other, so your application might be launched from another application or it might be launched from the system. An application can use a launch code to request that another application perform an action or modify its data. For example, a data collection application could instruct an email application to queue up a particular message to be sent.
TIP: In Palm OS 4.0 and higher, there are other ways for applications to communicate. See the section "When to Use the Helper API" to help you decide which method to use.
Sending a launch code to another application is like calling a specific subroutine in that application: the application responding to the launch code is responsible for determining what to do given the launch code constant passed on the stack as a parameter.
To send a launch code to another application, use the system manager function SysAppLaunch
. Use this routine when you want to make use of another application's functionality and eventually return control of the system to your application. Usually, applications use it only for sending launch codes to other user-interface applications.
For example, you would use this function to request that the built in Address Book application search its databases for a specified phone number and return the results of the search to your application. When calling SysAppLaunch
do not set launch flags yourself—the SysAppLaunch
function sets launch flags appropriately for you.
An alternative, simpler method of sending launch codes is the SysBroadcastActionCode
call. This routine automatically finds all other user-interface applications and calls SysAppLaunch
to send the launch code to each of them.
When an application is called using SysAppLaunch
, the system considers that application to be the current application even though the application has not switched from the user's perspective. Thus, if your application is called from another application, it can still use the function SysCurAppDatabase
to get the card number and database ID of its own database.
If you want to actually close your application and open another application, use SysUIAppSwitch
instead of SysAppLaunch
. This routine notifies the system which application to launch next and feeds an application-quit event into the event queue. If and when the current application responds to the quit event and returns, the system launches the new application.
When you allocate a parameter block to pass to SysUIAppSwitch
, you must call MemPtrSetOwner
to grant ownership of the parameter block chunk to the OS (your application is originally set as the owner). If the parameter block structure contains references by pointer or handle to any other chunks, you also must set the owner of those chunks by calling MemPtrSetOwner
or MemHandleSetOwner
. If you don't change the ownership of the parameter block, it will get freed before the application you're launching has a chance to use it.
In Palm OS 3.0 and higher, you can also use the Application Launcher to launch any application. For more information, see the section "Application Launcher" in the "User Interface" chapter.
WARNING! Do not use the
SysUIAppSwitch
or SysAppLaunch
functions to open the Application Launcher application.
Creating Your Own Launch Codes
The Palm OS contains predefined launch codes, which are listed in Table 2.1 at the end of this chapter. In addition, developers can create their own launch codes to implement specific functionality. Both the sending and the receiving application must know about and handle any developer-defined launch codes.
The launch code parameter is a 16-bit word value. All launch codes with values 0–32767 are reserved for use by the system and for future enhancements. Launch codes 32768–65535 are available for private use by applications.
Stopping an Application
An application shuts itself down when it receives the event appStopEvent
. Note that this is an event, not a launch code. The application must detect this event and terminate. (You'll learn more about events in the next chapter.)
When an application stops, it is given an opportunity to perform cleanup activities including closing databases and saving state information.
In the stop routine, an application should first flush all active records, then close the application's database, and finally save those aspects of the current state needed for startup. Listing 2.3 is an example of a StopApplication
routine from Datebook.c
.
Listing 2.3 StopApplication from Datebook.c
static void StopApplication (void) { DatebookPreferenceType prefs; // Write the preferences / saved-state information. prefs.noteFont = NoteFont; prefs.dayStartHour = DayStartHour; prefs.dayEndHour = DayEndHour; prefs.alarmPreset = AlarmPreset; prefs.saveBackup = SaveBackup; prefs.showTimeBars = ShowTimeBars; prefs.compressDayView = CompressDayView; prefs.showTimedAppts = ShowTimedAppts; prefs.showUntimedAppts = ShowUntimedAppts; prefs.showDailyRepeatingAppts = ShowDailyRepeatingAppts; // Write the state information. PrefSetAppPreferences (sysFileCDatebook, datebookPrefID, datebookVersionNum, &prefs, sizeof (DatebookPreferenceType), true); // Send a frmSave event to all the open forms. FrmSaveAllForms (); // Close all the open forms. FrmCloseAllForms (); // Close the application's data file. DmCloseDatabase (ApptDB); }
Notifications
On systems where the Notification Feature Set is present, your application can receive notifications and launch when certain system-level events or application-level events occur. Notifications are similar to application launch codes, but differ from them in two important ways:
- Notifications can be sent to any code resource, such as a shared library or a system extension (for example, a hack installed with the HackMaster program). Launch codes can only be sent to applications. Any code resource that is registered to receive a notification is called a notification client.
- Notifications are only sent to applications or code resources that have specifically registered to receive them, making them more efficient than launch codes. Many launch codes are sent to all installed applications to give each application a chance to respond.
The Palm OS system and the built-in applications send notifications when certain events occur. See the "Notification Summary" in this chapter for a complete list.
It's also possible for your application to create and broadcast its own notifications. However, doing so is rare. It's more likely that you'll want to register to receive the predefined notifications or that you'll broadcast the predefined sysNotifyHelperEvent
described in the "Helper Notifications" section.
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. An example of this is thesysNotifyAntennaRaisedEvent
for Palm VII™ series handhelds. A client might decide to handle the antenna key down event and in this case, setshandled
totrue
to inform other clients that the event has been handled. - 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 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 2.4.
Listing 2.4 Registering an application for a notification
SysNotifyRegister(myCardNo, appDBID, sysNotifySyncStartEvent, NULL, sysNotifyNormalPriority, myDataP);
If you are writing a shared library instead of an application and you want to be notified about the HotSync event, your call to SysNotifyRegister
looks slightly different. See Listing 2.5.
Listing 2.5 Registering a shared library for a notification
SysNotifyRegister(myCardNo, shlibDBID, sysNotifySyncStartEvent, SyncNotifyHandler, sysNotifyNormalPriority, myDataP);
The parameters you pass to the SysNotifyRegister
function specify the following:
- The first two parameters are the card number and 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. -
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 next parameter specifies how the notification should be received. This is where Listing 2.4 and Listing 2.5 differ.
Applications 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 shared library has no
PilotMain
function and therefore no way to receive a launch code, so it passes a pointer to a callback function. Only use a callback function if your code doesn't have aPilotMain
.Note that it's always necessary to pass the card number and database ID of your
prc
file even if you specify a callback function. -
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, Palm™ recommends using a value whose least significant bits are 0 (such as 32, 64, 96, and so on). -
myDataP
is a pointer to any data you need to access in your notification handler function. As with most launch codes,sysAppLaunchCmdNotify
does not provide access to global variables, so you should use this pointer to pass yourself any needed data.
After you've made the calls shown in Listing 2.4 and Listing 2.5 and the system is about to begin a HotSync operation, it broadcasts the sysNotifySyncStartEvent
notification to both clients.
The application is notified through the sysAppLaunchCmdNotify
launch code. This launch code's parameter block is 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.
The shared library is notified by a call to its SyncNotifyHandler
function. This function is passed the same SysNotifyParamType
structure that is passed through the launch code mechanism.
IMPORTANT: Because the callback pointer is used to directly call the function, the pointer must remain valid from the time
SysNotifyRegister
is called to the time the notification is broadcast. If the function is in a shared library, you must keep the library open. If the function is in a separately loaded code resource, the resource must remain locked while registered for the notification. When you close a library or unlock a resource, you must first unregister for any notifications. If you don't, the system will crash when the notification is broadcast.
Writing a Notification Handler
The application's response to sysAppLaunchCmdNotify
and the shared library's callback function are called notification handlers. 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 2.6 shows a notification handler for the sysNotifyTimeChangeEvent
notification that performs no work other than setting up a deferred notification (myDeferredNotifyEvent
) 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 2.6 Deferring notification within a handler
case sysAppLaunchCmdNotify :
if (cmdPBP->notify->notifyType == sysNotifyTimeChangeEvent
) {
SysNotifyParamType notifyParm;
MyGlobalsToAccess 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
-
sysNotifySleepNotifyEvent
-
sysNotifyEarlyWakeupEvent
-
sysNotifyLateWakeupEvent
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 wakeup 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 wakeup 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 2.7.
Listing 2.7 Responding to Late Wakeup Notification
case sysNotifyLateWakeupEvent: if ((Boolean) PrefGetPreference(prefDeviceLocked)) { SysNotifyRegister(myCardNo, myDbID, sysNotifyDeviceUnlocked, NULL, sysNotifyNormalPriority, NULL); } else { HandleDeviceWakeup(); } case sysNotifyDeviceUnlocked: HandleDeviceWakeup();
Helper Notifications
If the 4.0 New Feature Set
is present, the helper notification, sysNotifyHelperEvent
, is defined. This notification is a way for one application to request a service from another application. On Palm OS 4.0, 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 the chapter "Helper API" of the Palm OS Programmer's API Reference. 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.
This section discusses the helper APIs, which include the sysNotifyHelperEvent
notification and the data structures that it passes as the notifyDetailsP
portion of the SysNotifyParamType
structure. It covers:
When to Use the Helper API
If the 4.0 New Feature Set is present, 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" in this chapter), can use the Exchange Manager and Local Exchange Library to send data to another application (see the "Object Exchange" chapter), or can use the helper API to request that a service be performed. It can be difficult to determine which is the best method to use for your particular situation.
The helper API is best used in these circumstances:
- The 4.0 New Feature Set is present.
- 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 card number and local ID of the receiving database as well as which launch code to send.
- You want to communicate with any type of program.
Because the helper API uses a notification, the helper can be a shared library or another separately loaded code resource. Launch codes can only be received by applications. Because the Exchange Manager works through launch codes, it also only works with applications.
Requesting a Helper Service
Listing 2.8 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 advertise 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 2.8. - 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 2.8 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 the chapter "Helper API" of the Palm OS Programmer's API Reference 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 2.9 and Listing 2.10 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 2.9 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 2.10 show how the Dial application responds to the validate action.
Listing 2.10 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. - Remember that, as with all notifications, your notification handler does not have access to global variables. If there is data you need to access, pass it in the
userDataP
parameter toSysNotifyRegister
. If you want to have the notification handler return before the service is fully complete, make a copy of any data in the parameter block that you will need to complete the service.
Socket Notifications
Socket notifications allow an application to receive a notification when certain conditions change in a network socket—such things as a remote socket being closed, or a socket receiving TCP data. Socket notifications are part of a larger topic known as socket notices. Please note that there is more to registering for socket notifications than merely calling SysNotifyRegister()
. For details, see "Socket Notices," Palm OS Programmer's Companion, vol. II, Communications.
Launch Code Summary
Table 2.1 lists all Palm OS standard launch codes. These launch codes are declared in the header SystemMgr.h
. All the parameters for a launch code are passed in a single parameter block, and the results are returned in the same parameter block.
Table 2.1 Palm OS Launch Codes
Notification Summary
Table 2.2 lists all Palm OS standard notifications. 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 2.2 Notification Constants
The connection profile used by the Connection Panel has changed. |
|
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 system has been connected to or disconnected from the network. |
|
The Attention Manager has failed to post a virtual character to the key queue. |
|