Documentation  |   Table of Contents   |  < Previous   |  Next >   |  Index

3    Preferences

System Management

Exploring Palm OS®

The Preferences Manager handles both system-wide preferences and application-specific preferences. The Preferences Manager maintains preferences in two separate databases:

  • The "saved" preferences database contains preferences that are backed up during a HotSync operation. There is one "saved" preferences database that all applications use. This database contains all system-wide preferences as well as application-specific preferences.
  • The "unsaved" preferences database contains application-specific preferences that are not to be backed up during a HotSync operation. There is one "unsaved" preferences database that all application use.

This section describes how to obtain and set values for each of these preferences databases. It covers:

Accessing System Preferences ^TOP^

The system preferences specify how users want their Palm Powered handhelds to behave. For example, system preferences specify how dates and times are displayed and whether the system plays a sound when an alarm fires. These values are typically set using the built-in Preferences or Security application. Applications should, as a rule, respect the values stored in the system preferences.

To obtain the value of a system preference, use the PrefGetPreference() function and pass one of the SystemPreferencesChoice enum constants. For example, if an application's user interface displays the current date and time, it could do the following to find out how the user wants the date and time displayed:

TimeFormatType timeFormat = (TimeFormatType)    PrefGetPreference(prefTimeFormat); DateFormatType dateFormat = (DateFormatType)    PrefGetPreference(prefDateFormat);

Note that the PrefGetPreference function by default returns a uint32_t value. This return value must be cast to the appropriate type for the preference being returned.

Also note that the system preferences structure has been updated many times and maintains its own version information. Each Palm OS release that modifies the system preferences structure adds its new values to the end and increments the structure's version number. Palm OS Cobalt supports version 11 and earlier preferences; see the documentation on SystemPreferencesChoice for more information.

Setting System Preferences ^TOP^

Occasionally, an application may need to set the value of a system-wide preference. It is strongly recommended that you not override the system preferences without user input.

For example, suppose you are writing a replacement for the built-in Address Book application. The Preferences application contains a panel where the user can remap the Address Book hard key to open any application they choose. However, you want to make it more convenient for your users to remap the Address Book button, so you might display an alert that asks first-time users if they want the button remapped. If they tap Yes, then you should call PrefSetPreference() with the new value. The code might look like the following:

Listing 3.1  Setting a system preference


if (PrefGetPreference(prefHard2CharAppCreator !=  
      myAppCreatorId)) { 
   if (FrmAlert(MakeMeTheDefaultAlert) == 0) {  
      /* user pressed Yes */ 
      PrefSetPreference(prefHard2CharAppCreator,  
         myAppCreatorId); 
   } 
} 

Preferences in the User Interface

Table 3.1 shows the SystemPreferencesChoice constants and how they correspond to the values that users can set in the Preferences and Security applications. For further information about each preference, see the SystemPreferencesChoice enum documentation.

Table 3.1  Preferences set in Preferences and Security apps 

Application/Panel

Field

SystemPreferencesChoice Constant

Preferences application

General panel

Auto-off After

prefAutoOffDuration, prefAutoOffDurationSecs

Stay on in Cradle

prefStayOnWhenPluggedIn

System Sound

prefSysSoundLevelV20, prefSysSoundVolume

Alarm Sound

prefAlarmSoundLevelV20, prefAlarmSoundVolume

Alarm Vibrate1

prefAttentionFlags

Alarm LED1

prefAttentionFlags

Game Sound

prefGameSoundLevelV20, prefGameSoundVolume

Beam Receive field

prefBeamReceive

Preferences application Date & Time panel

Set Time Zone field

prefTimeZone

Daylight Saving

prefDaylightSaving
Adjustment

Preferences application

Formats panel

Preset to

prefCountry68K

Time

prefTimeFormat

Date

prefDateFormat, prefLongDateFormat

Week starts

prefWeekStartDay

Numbers

prefNumberFormat

Preferences application Buttons panel

Buttons on main panel

prefHard1CharAppCreator, prefHard2CharAppCreator, prefHard3CharAppCreator, prefHard4CharAppCreator, prefCalcCharAppCreator

Pen button

prefRonamaticChar

HotSync button

prefHardCradleCharApp
Creator
prefHardCradle2CharApp
Creator

Security application

Auto Lock Handheld

prefAutoLockType, prefAutoLockTime, prefAutoLockTimeFlag

Current Security

prefHidePrivateRecordsV33, prefShowPrivateRecords

Lock & Turn Off... button

prefDeviceLocked

1. The Alarm Vibrate and Alarm LED preferences only appear on handhelds that have the appropriate hardware capabilities.

Setting Application-Specific Preferences ^TOP^

You can use the Preferences Manager to set and retrieve preferences specific to your application. You do this by storing the preferences in one of two databases: the "saved" preferences database or the "unsaved" preferences database.

To write application preferences, you use PrefSetAppPreferences(). To read them back in, you use PrefGetAppPreferences(). Typically, you write the preferences in response to the appStopEvent when control is about to pass to another application. You read the preferences in response to a normal launch.

PrefSetAppPreferences() and PrefGetAppPreferences() take roughly the same parameters: the application creator ID, a preference ID that uniquely identifies this preference resource, a pointer to a structure that holds the preference values, the size of the preferences structure, and a Boolean that indicates whether the "saved" or the "unsaved" preferences database is to be used. PrefSetAppPreferences() also takes a version number for the preference structure. This value is the return value for PrefGetAppPreferences().

The following sections discuss the issues involved in using application-specific preferences:

When to Use Application Preferences ^TOP^

You use application preferences to store state specific to your application that should persist across invocations of your application. For example, the built-in applications store information about the last form and the last record or records displayed before control is switched to another application. This way, the user can be returned to the same view when he or she goes back to that application.

You can also use preferences for other values. You might allow the user to customize the way the application behaves and store such information in the preferences database. You might also use the preferences database as a way to share information with other applications.

Make sure that the preference values you choose are as concise as possible. In games, for example, it is often tempting to store a bitmap for the current state of the screen. Such a bitmap is over 25KB on a color handheld, and it is therefore best avoided. Instead, it is better to store items that let you recreate the current state, such as the player's position in pixels and the current level.

There are other ways to store values pertinent to your application. For example, you can store a single value as a feature using the Feature Manager. Note, however, that preferences (including those stored in the "unsaved" database) survive a soft reset because they reside in the storage heap. Features are deleted upon a soft reset. For this reason, preferences are more appropriate for storing application state than feature are.

Instead of storing application state values as preferences or features, you could also use a database that your application creates and maintains itself. If you choose this method of storing application preference values, you must write your own functions to read the preferences from the database and write the preferences to the database. If you want the preferences backed up, you need to set the backup bit. However, there may be cases where using your own database has advantages. See "Which Preferences Database to Use".

How to Store Preferences ^TOP^

Most applications store a single preference structure under a single preference resource ID. When the application receives an appStopEvent, it writes the entire structure to the resource using PrefSetAppPreferences(). When it receives a sysAppLaunchCmdNormalLaunch, it reads the structure back in using PrefGetAppPreferences().

Storing a single preference structure in the database is a convention that most applications follow because it is convenient to access the all preferences at once. Your application can store more than one preference resource, if you prefer. This requires more calls to PrefSetAppPreferences() and PrefGetAppPreferences(), but you may find it more convenient to use several preference resources if you have several variable-length preferences.

Which Preferences Database to Use ^TOP^

Both PrefGetAppPreferences() and PrefSetAppPreferences() take a boolean value that indicates whether the value is to be read from and written to the "saved" or the "unsaved" preferences database. To write the preference to the "saved" preferences database, set this boolean value to true. To write to the "unsaved" preferences database, set it to false.

The only difference between the two databases is that the "saved" preferences database is backed up when a user performs a HotSync operation, while the "unsaved" preferences database is not backed up by default. (The user can use a third-party tool to set the backup bit in the "unsaved" preferences database, which would cause it to be backed up.) Both the "saved" and the "unsaved" preferences reside in the storage heap and thus persist across soft resets. The only way that preferences are lost is if a hard reset is performed.

Use the "saved" preferences only for items that must be restored after a hard reset, and use the "unsaved" preferences for the current state of the application. For example, if your application has a registration code, you might write that to the "saved" preferences database so that the user does not have to look up the registration code and re-enter it after a hard reset. However, the loss of such items as the current form being displayed and the current database record being displayed isn't devastating, so they are written to the "unsaved" preferences database. For games, you might write the high score to the "saved" preferences database and any information about the current game to the "unsaved" preferences database.

It is important to use the "saved" preferences database sparingly. Any time that any application stores or changes a preference in the "saved" preferences database, the entire database is backed up during the next HotSync operation. For users with a large number of applications, this practice can potentially impact the amount of time that it takes to perform a HotSync operation.

Listing 3.2 shows the preferences structures and the StopApplication function from the HardBall application. The HardBallPreferenceType, which is written to the "saved" preferences database, only stores the high score information and accumulated time. All other preferences are stored in GameStatusType, which is written to the "unsaved" preferences database.

Listing 3.2  Saving application-specific preferences


typedef struct { 
   SavedScore highScore[highScoreMax]; 
   uint8_t lastHighScore; 
   uint8_t startLevel; 
   uint32_t  accumulatedTime; 
} HardBallPreferenceType; 
  
typedef struct { 
   enum gameProgress  status; 
   uint8_t  periodLength; 
   uint32_t nextPeriodTime; 
   uint32_t periodsToWait; 
   Boolean  paused; 
   uint32_t  pausedTime; 
   BrickType  brick[rowsOfBricks][columnsOfBricks]; 
   uint8_t  bricksRemaining; 
   uint8_t level; 
   WorldState last; 
   WorldState next; 
   RemovedBrick brokenBricks[brokenBricksMax]; 
   int16_t  brokenBricksCount; 
   uint8_t ballsRemaining; 
   Boolean movePaddleLeft; 
   Boolean movePaddleRight; 
   SoundType  soundToMake; 
   int8_t  soundPeriodsRemaining; 
   int32_t  scoreToAwardBonusBall; 
   Boolean lowestHighScorePassed; 
   Boolean highestHighScorePassed; 
   Boolean gameSpedUp; 
   Boolean  cheatMode; 
   uint32_t startTime; 
} GameStatusType; 
  
HardBallPreferenceType Prefs; 
static GameStatusType GameStatus; 
  
static void StopApplication (void) 
{ 
   ... 
   // Update the time accounting. 
   Prefs.accumulatedTime += (TimGetTicks() -  
      GameStatus.startTime); 
    
   // If we are saving a game resuming (it hasn't started  
   // playing yet) then preserve the game status. 
   if (GameStatus.status == gameResuming) { 
      GameStatus.status = SavedGameStatus; 
   } 
    
   // Save state/prefs. 
   PrefSetAppPreferences (appFileCreator, appPrefID,  
      appPrefVersion, &Prefs, sizeof (Prefs), true); 
    
   PrefSetAppPreferences (appFileCreator, appSavedGameID,  
      appSavedGameVersion, &GameStatus, sizeof (GameStatus),  
      false); 
       
   // Close all the open forms. 
   FrmCloseAllForms (); 
} 

If you have a large amount of preference data that must be backed up during a HotSync operation and is frequently changed, you could, as a performance optimization, store the preferences in a database that your own application creates and maintains rather than in the "saved" preferences database. This saves the user from having to have the entire "saved" preferences database backed up on every HotSync operation. The disadvantage of this technique is that you must write all code to maintain the database and to retrieve information from it.

Updating Preferences Upon a New Release ^TOP^

When you update your application, you may have new items that you want to store in the preferences database. You may choose to write a separate preference record to the database. However, it is better to update the current preference structure, size permitting.

The PrefSetAppPreferences() and PrefGetAppPreferences() functions use a versioning system that allows you to update an existing preference structure. To use it, keep track of the version number that you pass to PrefSetAppPreferences(). Add any new preferences to the end of the preferences structure, and then increment the version number. You might use a macro for this purpose:

#define CurrentPrefsVersion 2

When a user launches the new version of the application, PrefGetAppPreferences() is called before PrefSetAppPreferences(). The PrefGetAppPreferences() function returns the version number of the preference structure that it retrieved from the database. For example, if the new version is version 2, PrefGetAppPreferences() returns 1 the first time that version 2 of the application is run. If the returned version does not match the current version, you know that the user does not have values for the new preferences introduced in version 2. You can then decide to provide default values for those new preferences.

The first time any version of your application is run, PrefGetAppPreferences() returns noPreferenceFound. This indicates that the user does not have any preferences for the current application and the application must supply default values for the entire preferences structure. Listing 3.3 shows how the Datebook handles retrieving the version number from PrefGetAppPreferences().

Listing 3.3  Checking the preference version number


#define datebookPrefsVersionNum 4 
  
int16_t DatebookLoadPrefs (DatebookPreferenceType* prefsP) 
{ 
   uint16_t prefsSize; 
   int16_t prefsVersion = noPreferenceFound; 
   Boolean haveDefaultFont = false; 
   uint32_t defaultFont; 
    
   ErrNonFatalDisplayIf(!prefsP, "null prefP arg"); 
    
    
   // Read the preferences / saved-state information.  Fix-up if no prefs or  
   // older/newer version 
   prefsSize = sizeof (DatebookPreferenceType); 
   prefsVersion = PrefGetAppPreferences (sysFileCDatebook, datebookPrefID,  
      prefsP, &prefsSize, true); 
    
   // If the preferences version is from a future release (as can happen when  
   // going back and syncing to an older version of the device), treat it the  
   // same as "not found" because it could be significantly different 
   if ( prefsVersion > datebookPrefsVersionNum ) 
      prefsVersion = noPreferenceFound; 
    
   if ( prefsVersion == noPreferenceFound ) { 
      // Version 1 and 2 preferences 
      prefsP->dayStartHour = defaultDayStartHour; 
      prefsP->dayEndHour = defaultDayEndHour; 
      prefsP->alarmPreset.advance = defaultAlarmPresetAdvance; 
      prefsP->alarmPreset.advanceUnit = defaultAlarmPresetUnit; 
      prefsP->saveBackup = defaultSaveBackup; 
      prefsP->showTimeBars = defaultShowTimeBars; 
      prefsP->compressDayView = defaultCompressDayView; 
      prefsP->showTimedAppts = defaultShowTimedAppts; 
      prefsP->showUntimedAppts = defaultShowUntimedAppts; 
      prefsP->showDailyRepeatingAppts =  
         defaultShowDailyRepeatingAppts; 
  
      // We need to set up the note font with a default value for the system. 
      FtrGet(sysFtrCreator, sysFtrDefaultFont, &defaultFont); 
      haveDefaultFont = true; 
       
      prefsP->v20NoteFont = (FontID)defaultFont; 
   } 
    
   if ((prefsVersion == noPreferenceFound) || (prefsVersion <  
         datebookPrefsVersionNum)) { 
      // Version 3 preferences 
      prefsP->alarmSoundRepeatCount = defaultAlarmSoundRepeatCount; 
      prefsP->alarmSoundRepeatInterval = defaultAlarmSoundRepeatInterval; 
      prefsP->alarmSoundUniqueRecID = defaultAlarmSoundUniqueRecID; 
      prefsP->noteFont = prefsP->v20NoteFont; 
       
      // Fix up the note font if we copied from older preferences. 
      if ((prefsVersion != noPreferenceFound) && (prefsP->noteFont ==  
            largeFont)) 
         prefsP->noteFont = largeBoldFont; 
       
      if (!haveDefaultFont) 
         FtrGet(sysFtrCreator, sysFtrDefaultFont, &defaultFont); 
       
      prefsP->apptDescFont = (FontID)defaultFont; 
   } 
    
   if ((prefsVersion == noPreferenceFound) || (prefsVersion <  
         datebookPrefsVersionNum)) { 
      // Version 4 preferences 
      prefsP->alarmSnooze = defaultAlarmSnooze; 
   } 
    
   return prefsVersion; 
}