Thursday, March 29, 2007

How do i programatically launch another application from my application

You can programatically launch another application from within your application using the SysUIAppSwitch function by passing it the sysAppLaunchCmdNormalLaunch launch code and a NULL parameter block.
Here is a code example that shows how to launch the built-in MemoPad application:


static void MainGotoApplication()

{

LocalID theDBID;

UInt theCardNo;

DmSearchStateType theSearchState;

// Grab the id of the application we want to launch (the memo pad)

DmGetNextDatabaseByTypeCreator(true, &theSearchState, sysFileTApplication, sysFileCMemo, true, &theCardNo, &theDBID);

// Launch the new app.

SysUIAppSwitch(theCardNo, theDBID, sysAppLaunchCmdNormalLaunch, NULL);

}

Beaming Using the Palm OS Exchange Manager

As a final part of our "inter-application communication," let us discuss one more way of how to do it. As you may guess, I mean here the Exchange Manager API. This kind of communication differs from those ones we've spoke about in previous articles. When you hear 'beaming', yes—that's it. But, as you may discover, a request for exchange data is just one of the launch codes your application may receive (e.g. sysAppLaunchCmdExgReceiveData), so it may be useful to export data to other apps at the same device. So, let's go ahead and take a look at practical cases.


Case 1: Standard Beaming of a Whole Database or Specific Records:
The Palm OS's user interface doesn't allow beaming of an abitrary database, only prc-files, i.e. applications. So, it'd be nice for your program to offer such functionality. Fortunately, it becomes a pretty simple task with Exchange Manager. The normal working flow is as follows:
Define a variable of type ExgSocketType and initialize it to zeroes.
Assign appropriate values to description, target, and optionally to the type and name fields.
ExgSocketType is declared in ExgMgr.h as

typedef struct ExgSocketType {

UInt16 libraryRef; // identifies the Exg library in use

UInt32 socketRef; // used by Exg library to identify this connection

UInt32 target; // Creator ID of application this is sent to

UInt32 count; // # of objects in this connection (usually 1)

UInt32 length; // # total byte count for all objects being sent

UInt32 time; // last modified time of object

UInt32 appData; // application specific info

UInt32 goToCreator; // creator ID of app to launch

ExgGoToType goToParams; // If launchCreator then this

UInt16 localMode:1; // Exchange with local machine

UInt16 packetMode:1; // Use connectionless packet mode

UInt16 noGoTo:1; // Do not go to app (local mode only)

UInt16 noStatus:1; // Do not display status dialogs

UInt16 preview:1; // Preview in progress: don't

UInt16 reserved:11; // reserved system flags

Char *description; // text description of object

Char *type; // Mime type of object (optional)

Char *name; // name of object, generally a file name

} ExgSocketType;typedef ExgSocketType *ExgSocketPtr;

Note that the localMode field controls the operation mode, so you may want to set a 1 there to work locally.
To establish a connection, call ExgPut.
To send the whole database, call ExgDBWrite. An alternative way is to call ExgSend in a loop to send all desired data. Actually, these are the same methods because ExgDBWrite requires a callback function as a parameter, which uses ExgSend internally.
After all jobs are finished, call ExgDisconnect to close the connection.
The following code chunk illustrates all that was said above:

void BeamWholeDB(){
ExgSocketType exgSocket;
UInt16 cardNo;
LocalID dbID;
DmSearchStateType searchState;
Err err;

Char *appName = "TestDB";
err = DmGetNextDatabaseByTypeCreator(true, &searchState,'data', 'test', true,&cardNo, &dbID);
if (!err)
{
MemSet(&exgSocket,sizeof(exgSocket),0);
exgSocket.description = appName;
exgSocket.name = "TestDB.pdb";
err = ExgPut(&exgSocket);
if (err == 0)
{
ExgDBWrite(WriteProc, &exgSocket, appName, dbID,cardNo);
ExgDisconnect(&exgSocket,err);
}
}
}
As you can see, it's really not a big deal. Sure, we've left a lot of details behind the scene, such as sending multiple objects (refer to the count member of ExgSocketType), which application should be launched (if any) after receiving data, and so forth. Like any other Palm OS API, Exchange Manager API is rich, so you're welcome to investigate it more closely.
Okay, coming back to sending individual records, you may guess now that it differs from the previous example only in using ExgSend function in some loop instead of ExgDBWrite. Therefore, we'll consider the receiving part of flow; i.e. what the application should do when it is going to get some data via Exchange library.

A sequence of steps follows:
First, you should handle the sysAppLaunchCmdExgReceiveData launch code in PilotMain.
Call ExgAccept to accept the incoming request.
Call ExgReceive in a loop until it returns zero.
And finally, call ExgDisconnect to close the connection.

So, as we've just said:

// in PilotMainDmOpenRef g_dbP;

// our database...

if (cmd == sysAppLaunchCmdExgReceiveData){

DmOpenRef dbP;

if ((launchFlags & sysAppLaunchFlagSubCall) == sysAppLaunchFlagSubCall) {

dbP = g_dbP;

FrmSaveAllForms();

err = ReceiveData(dbP,(ExgSocketPtr)cmdBPB);

}

else

{

dbP = DmOpenDataByTypeCreator('data','test', dmModeReadWrite);

if (dbP) {

err = ReceiveData(dbP,(ExgSocketPtr)cmdBPB); DmClosedatabase(dbP);

}

}

}

Err ReceiveData(DmOpenRef dbP, ExgSocketPtr sockP){

Err err = 0;

UInt16 wIndex = -1;

err = ExgAccept(sockP);

if ( err == errNone ) {

char buffer[1024];

ULong lBytesRecv = 0;

while ((lBytesRecv = ExgReceive(sockP,buffer, sizeof(buffer)))

{

// process data ...

}

err = ExgDisconnect(sockP,err);

}

}
Now, a few words about the name member the of ExgSocketType structure. Starting from Palm OS 4.0, this field may contain a URL to identify the exchange library to connect with. Using a local exchange library may become handy if you want to debug your application locally or to send/request data to/from another application at the same device. On Palm OS 4.0 and later, if the URL starts from the '?' character, the user may be prompted to choose which exchange library to use if more than one library is registered for the selected scheme.
Case 2: Exchanging Data with the Local PIM
There are at least several situations when you might want to use Local Exchange Manager. A good example is exporting data to some built-in application (e.g. to MemoPad as the simpliest case). Even if the user has decided to use some third-party program instead of a standard one, you'll be sure that this program will correctly receive exported data. A small example looks like this:

void SendToLocalMemopad(){

ExgSocketType exgSocket;

Err err = 0;

Char *textBuf = "test";

UInt32 size = StrLen(textBuf) + 1;

MemSet(&exgSocket,sizeof(exgSocket),0);

exgSocket.description = "Test message";

exgSocket.name = "?_local:Sample.txt"; exgSocket.type = ".txt";

err = ExgPut(&exgSocket);

if (err == 0) {

ExgSend(&exgSocket,textBuf,size,&err);

ExgDisconnect(&exgSocket,err);

}

}
Well, after we've discussed the basic terms of Exchange Manager stuff, you are ready to go ahead and study additional details. Here I don't intend to copy the Palm OS Reference guide, so it'll be our homework. In real applications, you may want to use much more than we've covered here. Games, responding to 'Get' requests from a remote application, two-ways communications etc., are just instant examples. Good luck!
Communication with Devices Running an OS other than Palm OSOne other task that becomes relevant today is to exchange data between different devices with different OSes. Here, I'm speaking mainly about a PalmOS/Symbian <=> WinCE exchange. The main point is that Exchange manager uses the OBEX protocol to do its job. Early Pocket PC 2000 handhelds did not utilize OBEX at all. If you are facing such a case, you need to implement some kind of OBEX simulation on the Pocket PC device (refer to the OBEX specification for exact details). Fortunately, on Pocket PC 2002 and later, OBEX is supported, so data may be freely sent back and forth either from the OS GUI or programmatically. The only thing you might need to take care of in your Palm OS application is to register an application to handle desired types of data, say '.doc', by calling ExgRegisterDatatype. That's all, folks.

Steps for Configuring Java for Palm Os Application Development

1. Install a 'regular' j2se sdk (http://java.sun.com/j2se/1.5.0/download.jsp)
2. Install 'J2ME wireless toolkit' (WTK) from sun (
http://java.sun.com/products/j2mewtoolkit/download-2_2.html)
3. Install 'Websphere Everyplace Micro Edition for Palm' (WEME) (
https://www.developerpavilion.com/palmos/page.asp?page_id=365&tool_id=104) and copy the content of the directory Tools/lib installed to the lib/ subdirectory of your jdk (installed at step 1)
4. Install 'Palm OS Garnet 5.4 Simulator' from PalmSource (
https://www.developerpavilion.com/palmos/page.asp?page_id=365&tool_id=8)
Copy J9JavaVMMidp20.dll from Java_Tech_for_Garnet_WEME57\JVM\Simulator installed at step 3 in the same folder than PalmSim_54_rel.exe installed during previous step
Create a folder 'Autoload' in the same folder than PalmSim_54_rel.exe and copy inside it all the prc files from Java_Tech_for_Garnet_WEME57\JVM\Simulator (not all of them are needed actually but anyway...)
5. Install Eclipse (
http://eclipse.org/)
6. Install EclipseME (
http://eclipseme.org/) within eclipse and configuring the location of WTK (see EclipseME documentation : http://eclipseme.org/docs/installation.html)
Run Eclipse and create a new project of type 'J2ME Midlet Suite' dans 'J2ME' (ctrl-n) named HelloMIDlet
7. Create a new class within the project (right mouse button on HelloWorld project in the 'Package Explorer' view then 'New' and 'Class') named HelloMIDlet
Replace the generated content of the file HelloMIDlet.java with the Sun tutorial :
http://developers.sun.com/techtopics/mobility/midp/articles/wtoolkit/src/HelloMIDlet.java
Edit HelloWorld.jad by double-clicking on it, select 'Midelts' tab, click 'Add' then 'New Class' then 'New Midlet' then select HelloWorld and save (ctrl-s)
8. Create jar file (right mouse button on project, J2ME menu then 'create package') which will be created in the 'deployed' project subdirectory
9. Run jartoprc_w.exe from the Tools/bin of WEME installed at step 3, browse to select the jad file (not jar) copied by eclipseME in the 'deployed' directory and click 'generate PRC'
10. Run the simulator installed at step 4 (PalmSim_54_rel.exe)
11. Install the prc by drag & dropping it on the simulator window
12. Click on the icon to run the application within the simulator
13. For a real device you need another JVM installed :
http://www.palmone.com/us/support/jvm
14. They are some ways to automate the build process using ant tasks also, basically it uses antenna :
http://antenna.sourceforge.net/

Sending and Receiving Notifications

Notifications are a feature of Palm OS 3.5 and higher; they are sent by the system to notify interested applications that some event has happened or is happening. Applications can send notification as well. For information on sending notifications, see the section Sending Notifications below.

Receiving Notifications

For efficiency, your application registers only for notifications that you want to receive. You can find a list of available notifications in Palm OS Programmer's API Reference or in the header file NotifyMgr.h.

Different applications will register for notifications under different circumstances:

  • Sometimes you only want notifications when your application is running. For example, you might want to know that an expansion card has been inserted or removed so that you can update your display.
  • Sometimes you always want to be notified even when the user isn't running your application. In this case you would register when you get the sysAppLaunchCmdSystemReset launch code, and might never un-register. (If the user deletes your application, you are automatically unregistered from all notifications.)

The following example will show how to receive notifications when the system wakes up and goes to sleep (that is, when the handheld is turned on and off). These notifications could be used to track how a handheld is used or to monitor battery consumption, for example.

To register and use notifications, you need to do the following:

  • In your PilotMain function, add calls to the functions RegisterForNotifications and HandleNotification.
  • Create the RegisterForNotifications function.
  • Create the HandleNotification function.
  • Test the notification code.

Add Code to PilotMain

Add the following code to your PilotMain function's launch code switch statement, in the same place where you determine if the application was launched with a sysAppLaunchCmdNormalLaunch launch code:

   case sysAppLaunchCmdSystemReset:
   case sysAppLaunchCmdSyncNotify:  // sent when our app is installed
      RegisterForNotifications();
      break;
 
   case sysAppLaunchCmdNotify:
      HandleNotification((SysNotifyParamType *)cmdPBP);
      break;

With this code added to PilotMain, the RegisterForNotifications function will be called upon every system reset, as well as when your application has been installed via a HotSync process (but not necessarily when you've been installed by a debugger or when dragged into the Palm OS Emulator).

Be very careful when registering for notifications on system resets. If you have a bug in how you handle resets (like accessing global variables), you may crash the Palm OS and force another reset, creating an infinite loop. Many users won't know to do a no-notify reset so that they can delete your application.

Create the RegisterForNotifications function

Create the RegisterForNotifications function:

   static void RegisterForNotifications()
   {
      UInt16 cardNo;
      LocalID dbID;
 
      if (0 != SysCurAppDatabase(&cardNo, &dbID))
         return; // shouldn't ever fail, but just in case.
 
      // Tell the system we want to know when 
      // the device sleeps and wakes.
      SysNotifyRegister(cardNo, dbID, sysNotifySleepNotifyEvent, NULL,
        sysNotifyNormalPriority, 0);
      SysNotifyRegister(cardNo, dbID, sysNotifyLateWakeupEvent, NULL,
        sysNotifyNormalPriority, 0);
   }

With this RegisterForNotification function added, the HandleNotification function will be called upon every sleep and wake. Note that the fourth parameter to the SysNotifyRegister function allows you to pass a pointer to a callback function that will handle the notification. Passing NULL as the pointer to the callback function causes the sysAppLaunchCmdNotify launch code to be sent to your application (we had you add code to your PilotMain function to handle this above). In general, it is simpler to use launch codes, but if you know you are the active application or are writing a shared library you may want to use a callback function.

Create the HandleNotification Function

What you do in the HandleNotification function is really up to you, but for demonstration purposes we'll keep track of how long the device was asleep (for the last time it was asleep):

   #define kWhenWeWentToSleepFtr 1  // local definition for this routine
   #define kLastSleepLengthFtr   2  // read by your app somewhere else
 
   static void HandleNotification(SysNotifyParamType *np)
   {
      UInt32 timeAsleep;
 
      if (np->notifyType == sysNotifySleepNotifyEvent)
         FtrSet(appFileCreator, kWhenWeWentToSleepFtr, TimGetSeconds());
      else if (np->notifyType == sysNotifyLateWakeupEvent) {
         FtrGet(appFileCreator, kWhenWeWentToSleepFtr, &timeAsleep);
         FtrSet(appFileCreator, kLastSleepLengthFtr, TimGetSeconds()
               - timeAsleep);
      }
   }

Test the Notification Code

Now to prove the notification code works, add the following code to your main form's event handler. This code will display how long the system was asleep, reading the feature that was set in the example HandleNotification function.

   case frmOpenEvent: {
      char buffer[80];
      UInt32 sleepTime;
 
      frmP = FrmGetActiveForm();
      MainFormInit(frmP);
      FrmDrawForm(frmP);
 
      // Crudely draw on top of the form's contents.  (Just for a demo!)
      FtrGet(appFileCreator, kLastSleepLengthFtr, &sleepTime);
      StrPrintF(buffer, "Last sleep time: %ld seconds.", sleepTime);
      WinDrawChars(buffer, StrLen(buffer), 0, 16);
 
      handled = true;
      break;
   }
 

Sending Notifications

It is easy to broadcast a notification to inform other applications (or your own application) that something noteworthy has occurred. If no applications have registered for the notification that you are sending, your call to the SysNotifyBroadcast function will return quickly, though it does obviously take a little time for the system to make that determination.

  • You can send a notification immediately by using SysNotifyBroadcast. In this case, you'll wait until every recipient processes the notification.
  • Or else you can send a deferred notification by using SysNotifyBroadcastDeferred. In this case, the notification will be delivered later (currently, "later" means the next time EvtGetEvent is called).

The following code shows how to send a notification.

   // must be a registered creator ID; use your
    // application's or else register a new one
    // at http://www.palmos.com/dev/creatorid/
        #define myInterestingEvent 'MyEv'
 
 
   SysNotifyParamType notify;
 
   notify.notifyType     = myInterestingEvent;
   // say who is sending the event (i.e. you.)
   notify.broadcaster    = appFileCreator;
   notify.handled        = false;
   // or whatever additional information you need to send
   notify.notifyDetailsP = NULL;
 
        // or possibly use SysNotifyBroadcastDeferred
SysNotifyBroadcast(¬ify);

Tips from the VFS files

The good thing about the VFS (Virtual File System) APIs in Palm OS are that they work just like a traditional desktop-style file system, so they are usually fast to learn and add to your application's code. The bad thing is that many developers don't realize that the best practices for using the VFS APIs differ both from Palm OS database approach, as well as from traditional file system approaches. You can certainly use them under the database or desktop models, but with a little reflection you might well improve your application's UI, usability and/or speed.

One of the most obvious differences is that reading from external storage is considerably slower than reading from internal RAM, which is what you do when using Palm OS Databases. If your application was first created under the assumption that reading stored data was effectively instantaneous, you'll probably have already noticed that random access through one or more large VFS-resident files results in a, shall we say, suboptimal user experience. Therefore, consider going directly against your well-honed memory conservation instincts and try caching data in RAM - perhaps even large quantities of data, for extended periods of time. Nearly all devices that have VFS slots also have a megabyte or more of dynamic heap, so unless you're already putting that space to good use, try caching it there. Alternatively investigate reading it in to the storage heap: most users have lots of space left there for you to use either Feature Pointers or else a temporary database. If your files are normal Palm OS databases and they're only on the card for convenience purposes, consider caching the entire database in RAM while you're running ? but make sure you delete the cache both when you quit as well as after a reset in case you crash (no offense implied!) or the user hits the reset button for some reason.

Another big difference between in-RAM databases and files is that simply finding your data takes more time. DmGetNextDatabaseByTypeCreator is really fast, but doing the simple equivalent with a file (assuming you haven't a unique file extension you're looking for) requires that you open each file and read the first 64 bytes. If you're looking within the /palm/launcher directory, your performance will drop the more applications the user installs on their card. This is a good time to remember that the user normally cares about perceived performance, not actual performance. Therefore if you really do need to scan every file in /palm/launcher to find your data, don't do that when the application launches but instead generate a list of files and then check them one by one during nilEvents. For example if you're showing thumbnails of pictures, get a directory listing and draw the first one's thumbnail immediately, but then return to your main event loop and have it call EvtGetEvent with a very short timeout. When you get a nilEvent, read the next file and draw its thumbnail, and repeat. (When you're done, make sure to change the timeout to be unlimited to save battery.) The total time spent will be about the same, but the user will not have to wait for it all to finish. Even better, if you don't expect the data to change often, cache the thumbnails along with their filenames and modification dates so if it looks like nothing has changed, then you can draw that first display before you open up a single file. Of course you'll need to adjust the rest of your application to know about these tricks, and ensure the current data has been read in at some point, but the speedups can be truly dramatic.

When designing a system to use VFS, you need to decide whether to keep your data in Palm OS database formats (i.e. PRC or PDB files) or in some other traditional file format (e.g. a .TXT file). Your users will probably appreciate being able to install large data files either on a card or internally, which is a vote for using databases in both places. There are utility VFS routines to read the contents of a record or resource from a card-resident PDB, but writing is problematic in such a condition because changing the size of a record or resource can mean that nearly all the file would have to be rewritten. (Consider reading the entire database into RAM and then saving the whole thing again with VFSExportDatabaseToFile, or else parsing the file yourself if performance requires it. The PDB/PRC format is well documented.) On the other hand, you might not be in control of the data files, if you're doing a photo viewer as described above you have to use the same format as the camera did. In that case your task is determining whether to also support saving the photos in RAM or not, in a different container.

If you're coming from a traditional desktop file system mindset, the biggest differences between that and VFS are in the user experience, but there are a few programming aspects too. The user experience when using VFS will ideally be indistinguishable from using internal RAM, and this is usually achievable with care and by using some of the aforementioned ideas. Entering filenames or using file browsers is natural to techie types like us, and might even be requested by users who don't realize there is a better way, but many superior interfaces recognize that files are a means to an end: the focus should be on working with the user's data, not on managing filenames and directories.

On a desktop system you also don't need to worry much about the user pulling out the hard drive that you're in the middle of using, or installing new ones while you're running. But on handheld devices, that needs to be easy and natural. While you're running, you should register for the sysNotifyVolumeMountedEvent and sysNotifyVolumeUnmountedEvent notifications and smoothly show the additional data or gracefully stop using any open files. Telling the user to put back the card is an option to be avoided if possible, since they might not notice your alert until much later, and then you've got to deal with the possibility of the card (or file) having been modified in some other system in the interim.

Also consider locality of reference when reading files, and the number of bytes you read at a time. Palm OS devices may or may not have an automatic low-level cache for the card, so reading a byte at a time might incur considerable overhead. It may well be better to read say 32k of data into your own cache: try some timing tests for your own scenarios.

Palm OS Databases are fast to search and read, and their characteristics are well suited for handheld use and UI. Files can be just as good if you take the time to provide a great user experience... as well as cache appropriately and work some idle-time sleight of hand.