USERNAME PASSWORD LOST PASSWORD? REGISTER
"A Complete Mobile Application Development Environment"
Advertisement

Downloads
Documentation
Forums
Blog
Press
Bug Tracking
Creator IDs
Contact Us




Improving Usability PDF Print E-mail

There are a number of ACCESS Linux Platform components that you can employ within your applications to increase it's degree of "polish" and improve its overall usability. Some of these components help your application work well with others, while other components simply make your application a better ACCESS Linux Platform application.

This chapter includes the following sections:

Supporting Find
Alerts and Alarms
Preferences and Global Settings
Power Management
Device-Specific Information

Supporting Find ^TOP^

The ACCESS Linux Platform includes a system-wide "find" facility that allows the end user to quickly and easily search for text throughout the various applications on their device. It works by sending an ALP_APP_FIND ("--alp-find") launch code to applications that have "opted in" to the find facility. Each application, upon receiving this launch code, is then responsible for searching its data for the indicated string and informing the Find Manager of each "hit".

Architecting the system in this way has a number of advantages, including:

  • Only those applications that might actually provide results will be called upon to search their data, improving the efficiency of the find operation.
  • Applications that cannot meaningfully respond to a find request won't receive the ALP_APP_FIND launch code, and thus don't have to be written to handle it.
  • Because each application searches its own data, application data doesn't have to be structured so that it can be searched externally. Each application can decide for itself whether it has any matches with the supplied search string.
  • The find results are displayed incrementally, as they are reported by the various applications. The user can cancel the find at any time by again selecting the Find icon in the Status Bar.

The Find Manager sends the ALP_APP_FIND launch code to applications one at a time. If the user cancels the operation, the application currently processing the launch code will receive ALP_APP_FIND_CANCEL ("--alp-find-cancel"), and ALP_APP_FIND will not be sent to any other applications.

Figure 10.1 shows the ACCESS Linux Platform find dialog, with a search string and three "hits" from two different applications. For each "hit", the dialog shows some or all of the text in which the search string was deemed to occur, along with the name of the application returning the result. If the user selects one of the search results, that application is launched with a request to display the relevant data containing the search results.

Figure 10.1  System Find dialog

Registering with the Find Manager ^TOP^

To indicate to the Find Manager that your application can contribute results when the user employs the system-wide find facility, add the following entry to your application's Manifest.xml file, within the <Application> element:

<FindVersion>1.0</FindVersion> 

The numeric value indicates the version of the Find Manager that your application is written to support. Currently, the only allowed value is "1.0".

Once you have added this line to your Manifest.xml file and built your application, it will receive an ALP_APP_FIND launch code whenever the user selects the "find" icon.

Responding to a Find Request ^TOP^

The ALP_APP_FIND launch code is accompanied by two parameters. The argv array looks like this (the index values shown here assume that the launch request contains no additional launch codes):

argv[0] = bar:com.access.apps.sample
argv[1] = --alp-find
argv[2] = 1.0
argv[3] = search_string

Immediately following the launch code is the Find Manager version number. The final parameter is the one your application is most interested in: the string being searched for.

The basic procedure for handling the ALP_APP_FIND launch code is as follows:

  1. Notify the Find Manager that you'll (potentially) be sending back results.
alp_find_results_start();
  1. Search your application's data. Each time you find a match against search_string, let the Find Manager know.
err = alp_find_results_submit_one(
    NULL,    // app to launch (NULL = me)
    "myResult",	    // result key
    NULL,		    // icon (NULL = use launcher icon)
    "Result1",	    // primary search result
    "My App	"    // secondary search result
);

The first three parameters allow the user to launch (or relaunch) an application (usually, yours) by tapping the search results. Usually, you'll set the first parameter to NULL, indicating that your application (that is, the one returning the search results), should be relaunched in that instance. The second parameter is passed to the application, following an ALP_APP_DISPLAY launch code, when the application is launched or relaunched. It should be a unique key that identifies the found data. The third parameter specifies the icon to display along with the search results: set it to NULL to use the icon your application displays in the Launcher.

The final two parameters specify the two lines of text to be displayed as the search results. The primary search result is usually the text containing the search string (or a portion of that string, if the string it too long to reasonably fit within the Find dialog). However, it needn't be the actual text; your application can specify any string that would make sense to the user here. The same goes for the secondary search result: it can be anything that your application deems meaningful to the end user. However, by convention it is usually the name of your application, as shown in Figure 10.1.

  1. Check the result of the alp_find_results_submit_one() call. If it is ALP_STATUS_FIND_CANCELLED, the user has canceled the search. Accordingly, stop looking for the search_string and call alp_find_results_end(). Any search results submitted after the user has canceled the operation will be ignored.
  2. Call alp_find_results_end() to allow the Find Manager to proceed to the next application.

Note that you'll need to put the following #includes in your source file:


#include <alp/sysclass.h> 
#include <alp/find.h> 

The second line should be obvious; the first is needed because find.h defines a constant (ALP_STATUS_FIND_CANCELLED) using a value defined in sysclass.h.


IMPORTANT: Because you are employing APIs exported by the Find Manager, and because the Find Manager is encapsulated within its own library, you'll need to make sure that alp_find is included among the libraries listed on the LIBRARIES line of your application's makefile. Otherwise, the link step will fail.

Reporting Progress

As the search progresses, the Find Manager displays the status of the search on the line immediately beneath where the user entered the search string (in Figure 10.1, this is the line that says "Found 3 items"). Each progress message overwrites the previous one.

As each application is called upon to search its data, the Find Manager identifies the application in the progress line. When all applications have completed their searches, the Find Manager uses this line to indicate the total number of items found.

Your application can cause additional progress messages to be displayed. For instance, you might want your application to indicate how many "hits" it found. Submit additional progress messages by calling alp_find_results_submit_progress(), like this:


char found_string[64]; 
snprintf(found_string,64, "found %d matches...", numMatches); 
alp_find_results_submit_progress(found_string); 

Allowing the User to Cancel the Search

The user can cancel the search operation at any time by clicking on the "magnifying glass" icon in the status bar. When the user does this, applications that have not yet been told to search their data are skipped, and the application currently processing the ALP_APP_FIND launch code is informed that the user has canceled the operation.

The application is informed of the cancellation in two ways:

  • Calls to alp_find_results_submit_one() return ALP_STATUS_FIND_CANCELLED.
  • The application is relaunched with an ALP_APP_FIND_CANCEL ("--alp-find-cancel") launch code.

Although any search results submitted to the Find Manager after the user has canceled the operation are simply ignored, it is good programming practice to discontinue the search as soon as reasonably possible after the application has been informed of the cancellation.

Rather than dealing with both the return value from alp_find_results_submit_one() and the ALP_APP_FIND_CANCEL launch code, you may find it simpler just to set a global variable if the ALP_APP_FIND_CANCEL launch code is received, and discontinue searching if you notice that the global variable has been set. That is, you might handle the two launch codes something like this:


if(argc > 1 && !strcmp(ALP_APP_FIND, *argv))		{ 
	g_find_canceled = 0; 
	do_find(argv); 
} else if(!strcmp(ALP_APP_FIND_CANCEL,*argv)){ 
	g_find_canceled = 1; 
} 

Within your "do_find()" function, exit whenever you notice that g_find_canceled is set. Since searching is often done in a loop, you can include a check for this global variable in your loop, and exit when it is nonzero.

Whether your search completes or is canceled prematurely by the user, always call alp_find_results_end() to signal to the Find Manager that you are no longer looking for the search string.

Alerts and Alarms ^TOP^

Setting an Alarm ^TOP^

The Alarm Manager is a relatively simple mechanism for notifying applications of real-time alarm events. It goes beyond the single alarm used in standard Linux by maintaining a SQLite database of alarms; many applications can have alarms set, and an individual application can have multiple alarms set.

Part of the Hiker Application Framework, the Alarm Manager provides developers with a standard way to request that an alarm be triggered at a particular time. An application needn't be running in order to receive an alarm notification: the Alarm Manager notifies both active and inactive applications when their alarms are triggered. As well, the Alarm Manager will wake the device if an alarm occurs while the device is asleep.

The Alarm Manager has no UI of its own: it doesn't provide reminder dialog boxes, and it doesn't play the alarm sound. The Alarm Manager simply sends an ALP_APP_ALARM launch code to the appropriate application, and it is up to the application do take the appropriate action. Applications that need to bring an alarm to the user's attention must do this through the Attention Manager.

Register for an alarm by calling alp_alm_set_alarm(). You pass this function three parameters:

  • The package name of the calling application. This is the application that will be informed when the specified alarm time is reached. For instance, "bar:com.access.apps.test".
  • A 32-bit value that your application uses to identify the alarm. The Alarm Manager uniquely identifies each alarm by the combination of the package name and this 32-bit "reference number," so the reference number for each alarm only has to be unique within the scope of a given application.
  • The alarm time. Specify the date and time as the number of seconds number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC). This is the standard established by the time_t data type declared in the GNU C library's time.h file. Thus, you can use the functions declared in time.h to get and manipulate time values for use with the Alarm Manager.

To cancel an existing alarm, call alp_alm_set_alarm() again with the package name and reference number that identify the alarm to be canceled, but supply a value of zero for the alarm time.


IMPORTANT: When working with any of the Alarm Manager APIs, be sure that alp_alarmmgr is included among the libraries listed on the LIBRARIES line of your application's makefile. Otherwise, the link step will fail.

Listing 10.1 shows a function that sets an alarm to go off 10 seconds from when the function is called.

Listing 10.1  Setting an alarm


#include <string.h> 
#include <time.h> 
#include <hiker/alarmmgr.h> 
 
void ButtonClicked(GtkWidget* button, gpointer data) 
{ 
	time_t now; 
	time_t then; 
 
	// set the alarm for 10 seconds from now. 
	now = time(NULL); 
	then = now + 10;    // 10 seconds from now 
 
	alp_alm_set_alarm("bar:com.access.apps.alarmtest",  
		0, then); 
} 

Note that this code uses a constant value for the alarm identifier (the second parameter passed to alp_alm_set_alarm()). This is fine if the application only ever sets a single alarm at a time. However, if alp_alm_set_alarm() is called once to set an alarm, and then called again with the same alarm identifier value before the first alarm time is reached, the second call to alp_alm_set_alarm() will overwrite the settings from the first call; it will be as if the first alarm setting had never been made.

When the alarm time is reached, the application identified in the first parameter ("bar:com.access.apps.alarmtest", in the above example) is launched (or relaunched, if it is currently running) with a launch code of ALP_APP_ALARM ("--alp-alarm") and a single parameter, the 32-bit alarm identifier you specified when calling the alarm set function. Thus, in addition to keeping the alarms distinct in the Alarm Manager database, the alarm identifier allows you to quickly and easily identify which alarm was triggered when you receive the ALP_APP_ALARM launch code.

Responding to an Alarm ^TOP^

When the alarm time is reached, your application will be launched (or relaunched) with an ALP_APP_ALARM ("--alp-alarm") launch code. The argv parameter following the launch code is the alarm identifier value specified when alp_alm_set_alarm() was called. This allows your alarm handling code to distinguish between alarms. Note that you can obtain the alarm time by calling alp_alm_get_alarm().

If the ALP_APP_ALARM launch code is sent to your application for some reason other than that the specified time has been reached, the argv value following the alarm identifier value will contain ALP_APP_ALARM_REASON ("--alp-alarm-reason") and the argv value after that will contain an indication of the reason for the launch code. This will be one of the ALP_ALARM_REASON_... values declared in alarmmgr.h. Generally, these unusual situations arise either because the system clock has been advanced (resulting in the scheduled alarm expiring earlier than expected), or the Alarm Manager has been restarted and now finds that one or more scheduled alarms have expired since the last time it was running. But regardless of the reason, either the alarm was just triggered or the specified alarm time has passed.

Regardless of why your application was given the ALP_APP_ALARM launch code, unless you are being relaunched your application is not the primary application (ALP_APP_PRIMARY is not among the launch codes you will have received). Thus, you should handle the launch code as quickly and efficiently as possible.

You can use alarms for any number of reasons. For instance, an email client might set periodic alarms (every 5 minutes, perhaps) to signal that it should check the server for new messages. Often, however, the alarm needs to be communicated to the user. For that, most applications should employ the Attention Manager, as detailed in "Getting the User's Attention."

Clearing an Alarm ^TOP^

Once an alarm is triggered, it is automatically removed from the Alarm Manager's database. Otherwise, alarms persist in the Alarm Manager database until they are explicitly removed or until the specified application is removed from the device. Exiting the application does not affect registered alarms.

If you are using alarms to regularly perform some action, you may want to cancel those alarms when the user exits the application. Or, if your application lets the user set alarms, it will likely need to provide a way to allow the user to clear them as well. Whatever the reason, clearing an existing alarm is a simple process: call alp_alm_set_alarm() again with the same first two arguments (the package name and the 32-bit alarm identifier), but set the third argument—the alarm time—to zero. For example, in our Alarm Test application, where we used a value of zero for the 32-bit alarm identifier, we could clear the alarm we set like this:


alp_alm_set_alarm("bar:com.access.apps.alarmtest", 0, 0); 

Note that you will need to call alp_alm_set_alarm() for each of the application's alarms that you wish to clear.

Getting the User's Attention ^TOP^

If your application is running, there are of course many ways you can get the user's attention: eye-catching visuals, sounds, and the like. However, there are often times when you need to bring something to the user's attention in such a way that the user can choose not to deal with the issue right then and there. For instance, notifying the user of a pre-set appointment. This is something that should be brought to the user's attention at the soonest possible opportunity after the specified date and time, and is something that the user may choose to ignore for a while. As explained in the section titled "Attention Manager," this is just the kind of task that the Attention Manager was designed for.


IMPORTANT: When working with any of the Attention Manager APIs, be sure that alp_attnmgr is included among the libraries listed on the LIBRARIES line of your application's makefile. Otherwise, the link step will fail.

If you look at the public header file for the Attention Manager (attnmgr.h, in the Hiker framework), the interface appears to be nearly as simple as the Alarm Manager. This apparent simplicity is somewhat deceptive, however. In truth, the Attention Manager is highly customizable, giving application a great deal of control over how "alerts" are presented to the user. This power is largely controlled by the properties argument to the alp_attn_alert_post() and alp_attn_alert_post_with_callback() functions.

The use of the Attention Manager is perhaps best illustrated through an example. The previous sections (beginning with "Setting an Alarm") showed how to set an alarm to trigger at a specific point in time. In response to the alarm being triggered, many applications will want to post an alert to the Attention Manager for presentation to the user. The code in Listing 10.2, below, shows how you can do just this.

Listing 10.2  Posting an alert when an alarm is triggered


#include <string.h> 
#include <time.h> 
#include <hiker/alarmmgr.h> 
#include <hiker/attnmgr.h> 
 
#include <assert.h> 
 
#include <gtk/gtk.h> 
#include <glade/glade.h> 
 
#include <alp/appmgr.h> 
#include <alp/bundlemgr.h> 
 
#define kAlarmTestXML "alarmtest.glade" 
 
// main window "destroy" handler 
void CloseTheApp(GtkWidget* widget, gpointer user_data) { 
	gtk_main_quit(); 
} 
 
// main window exit handler 
void ExitTheApp(gpointer cbData) { 
	gtk_main_quit(); 
} 
 
// button "clicked" handler 
void ButtonClicked(GtkWidget* button, gpointer data) { 
	time_t now; 
	time_t then; 
 
	// set the alarm for 10 seconds from now. 
	now = time(NULL); 
	then = now + 10;	// 10 seconds from now 
	alp_alm_set_alarm("bar:com.access.apps.alarmtest", 0, 
		then); 
} 
 
int alp_main(int argc, char *argv[]) { 
	GladeXML* xml; 
	bool regularLaunch; 
	int i; 
	alp_status_t err; 
 
	AlpAttnProp p0 = {"title", "title"}; 
	AlpAttnProp p1 = {"markup-1", "mark<u>up</u>-1"}; 
	AlpAttnProp p2 = {"smarttext-2", "(650) 555-1212"}; 
	AlpAttnProp p3 = {"smarttext-3", "http://www.google.com"}; 
	AlpAttnProp p4 = {"text-4", "text-4"}; 
	AlpAttnProp p5 = {"text-5", "text-5"}; 
	AlpAttnProp p6 = {"text-6", "text-6"}; 
	AlpAttnProp p7 = {"image", "clock.png"}; 
	AlpAttnProp *properties[] = { 
		&p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7 
	}; 
	uint32_t nprops = sizeof(properties) / 
		sizeof(properties[0]); 
	 
	regularLaunch = false; 
	for(i = 1; i < argc; i++){ 
		if(strcmp(argv[i], ALP_APP_PRIMARY) == 0){ 
			regularLaunch = true; 
		} else if(strcmp(argv[i], ALP_APP_ALARM) == 0){ 
			// argv[i+1] contains the 32-bit alarm identifier 
			time_t now = time(NULL); 
		 
			err = alp_attn_alert_post( 
				"bar:com.access.apps.alarmtest", 
				"alp/alarmtest/alarm", 
				"aUniqueString", 
				NULL,			// interface file URL (none) 
				ALP_ATTN_PRIORITY_NOTICE,   // priority 
				0,				// no timeout 
				nprops,		 
				properties		 
				); 
		} 
	} 
	 
	if(regularLaunch){ 
		// init the system 
		gtk_init(&argc, &argv); 
		 
		// create the main window 
		gchar* resource_name = alp_bundle_ref_ro_pathname( 
			alp_bundle_ref_me(), kAlarmTestXML); 
		xml = glade_xml_new(resource_name, NULL, NULL); 
		g_free(resource_name); 
		assert( xml != NULL ); 
	 
		// hook all handlers 
		glade_xml_signal_autoconnect(xml); 
		 
		// hook the ALP exit handler 
		alp_app_add_exit_handler(ExitTheApp, NULL); 
		 
		// run 
		gtk_main(); 
	} 
	 
	return 0; 
} 

As you can see in alp_main(), posting the alert only requires a single function call. But constructing the properties array that is passed in that call takes a number of lines of code.

Figure 10.2 shows the alert item dialog that is presented to the user ten seconds after they press the button to set the alarm in the above application1. Note that the dashed lines in the figure do not appear on-screen; they are included to identify the areas of the dialog that are specified using the properties array.

Figure 10.2  Attention Manager alert item dialog

As you can surmise from looking at the above code (in Listing 10.2), the properties array consists of zero or more pointers to key-value pairs, where each key and each value is a C string. The order of the entries in the properties array is not important; the Attention Manager only cares about the value for each key.

The Alert Item Dialog Title

The dialog title, as you might guess, is set using the "title" property:


AlpAttnProp p0 = {"title", "title"}; 

In our example, the value of this key is the same as the name of the property ("title"), although you would normally supply a more meaningful title string. Or, you can omit the title by not supplying a "title" key-value pair.

Icons and Images

Moving on to the image on the left side of the dialog, here we have set it with the "image" key:


AlpAttnProp p7 = {"image", "clock.png"}; 

You actually have a couple of choices here. As in our example, you can supply an image using any of the formats that can be displayed by GTK+: JPEG, PNG, GIF, and the like. The supplied path is relative to the rsc directory within your application bundle (and, of course, you need to be sure that the image is listed in your makefile so that it gets placed into the bundle when your project is built). You can specify an absolute path (to retrieve the image or icon from a location other than within your own bundle) using either of these two formats:


bar:com.access.apps.alarmtest/rsc/clock.png 
file:///opt/alp/data/clock.png 

The image you supply can be a generic one for the application, or an image that is specific to the alert. For instance, an SMS application might use this to display the icon that represents the person sending an SMS message.

Alternatively, you can supply an icon, using the "icon" key, or an animated image using the "animated-image" key. Why would you choose one over the other? The use of the animated image key over the others should be obvious. As for the "icon" key, the difference lies in how the alert is displayed in the Attention Manager's list dialog. The item dialog takes up nearly the full screen, and can present a title, an image, up to 8 lines of text, and up to four buttons, all for a single alert. As shown in Figure 10.3, the list dialog presents your alerts in a much more compact form, showing only three lines of text and an icon2. Therein lies a clue: the list dialog doesn't show an image, only an icon. If you supply an "image" or an "animated-image", neither will be displayed in the list dialog entry for that same alert. Here is what is displayed for the various combinations of "image" (or "animated-image") and "icon" entries in the properties array:

image and icon
Alert item dialog shows the image. List dialog shows the icon.
image only
Alert item dialog shows the image. List dialog shows a default icon (see Figure 10.3).
icon only
Alert item dialog shows the icon. List dialog shows the same icon.
Neither
Alert item dialog shows a default icon. List dialog shows a default icon.
Background Image

You can specify a background image to be displayed in the alert item dialog by including in the property list the ALP_ATTN_PROP_DIALOG_BACKGROUND_URL key, with a path to the image as the value. The image must be stored within your application's bundle, and the supplied path must be relative to the bundle.

Figure 10.3  Attention Manager list of active events

The Text Areas

There are a number of keys that can be used to specify the contents of the text area in both the detail and list dialogs. They are as follows (where n in the keys represents a digit from 1 to 6):

text-n
A line of plain text. For the property's value, either supply the actual text or a path, relative to the bundle's rsc directory, to a resource file from which the text is to be loaded.

AlpAttnProp p4 = {"text-4", "text-4"}; 

markup-n
A line of text that is formatted using embedded HTML tags. For the property's value, either supply the actual text or a path, relative to the bundle's rsc directory, to a resource file from which the text is to be loaded.

AlpAttnProp p1 = {"markup-1", "mark<u>up</u>-1"}; 

smarttext-n
A line of "smart text." The smart text engine recognizes certain patterns within the supplied text (for instance, phone numbers or URLs) and turns them into active links that can be selected by the user. For the property's value, either supply the actual text or a path, relative to the bundle's rsc directory, to a resource file from which the text is to be loaded.

AlpAttnProp p2 = {"smarttext-2", "(650) 555-1212"}; 

To keep things simple, think of the digit after the dash for each of the above keys as specifying the line on which the text should be displayed. Because n is limited to 6, this allows you to display up to six lines of text, as illustrated in our sample code. In fact, though, you can display more lines by specifying multiple keys with the same digit after the dash (and note that longer lines of text wrap, and thus consume more lines on-screen). While you cannot supply two values for the same key ("text-1", for instance), you can define values for both "text-1" and "markup-1". If you supply multiple keys with the same digit after the dash, the Attention Manager's alert item dialog presents them in the following order:

  • text
  • markup
  • smarttext

Suppose you defined your properties array like this:


AlpAttnProp p1 = {"markup-1", "Displayed on line 2"}; 
AlpAttnProp p2 = {"text-1", "Displayed on line 1"}; 
AlpAttnProp p3 = {"text-4", "Displayed on line 3"}; 

When the alert item dialog is displayed, the text will be presented in the order indicated by the text values for each key. That is, the value for "text-1" will appear first, followed by the value for "markup-1", followed by the value for "text-4". There will be no intervening blank lines, unless the value you specify for one of the keys is the empty string.

What is displayed in the list dialog is more tightly controlled and needs to be taken into account when choosing which keys to use to present your text. In the Attention Manager's list dialog, each item is represented by an icon and a maximum of three lines of text. The list dialog always displays one line of text from the keys ending with "-1", followed by one line of text from the keys ending with "-2", followed by one line of text from the keys ending with "-3". It never displays any of the text for the keys ending with "-4", "-5", or "-6". As well, if you provide values for multiple keys ending with "-1", for instance, only one will be displayed. If "text-1" is provided, its text is displayed. If not, but "markup-1" is provided, its text is displayed. If it isn't provided, either, then "smarttext-1" is displayed. If none of them are provided, the line is omitted. That is, if you specified the following:


AlpAttnProp p1 = {"text-1", "Displayed on line 1"}; 
AlpAttnProp p2 = {"text-3", "Displayed on line 2"}; 
AlpAttnProp p3 = {"text-5", "Not shown in list dialog"}; 

As the values for the strings state, you'd only see two lines of text in the list dialog, one after the other (the third line would be blank); the value of text-1 immediately followed by the value for text-3.

Be aware that while the alert item dialog allows long lines of text to wrap, the list dialog does not. Each line of text is truncated.

Finally, note that the value you supply for the priority argument to the alp_attn_alert_post() function call controls whether or not the alert is presented using an alert item dialog, and whether or not it appears in the alert list dialog.


IMPORTANT: Be sure to carefully test each alp_attn_alert_post... call to ensure that each alert appears as you intended in the alert item dialog and/or the list dialog, as appropriate.

The Button Bar

Most alert item dialogs will stick with the three standard buttons shown in Figure 10.2. These three buttons behave as follows when clicked:

OK
Clears the alert. The alert item dialog is closed, and the alert is removed from the database. Because it is no longer in the database, it is not displayed in the list dialog.
Snooze
The alert item dialog is dismissed for a period of time (5 minutes, by default). The alert remains in the database and is displayed in the list dialog. After the snooze interval, the alert item dialog is redisplayed.
Go To
The alert item dialog is dismissed, the item is cleared from the alert database, and the application specified when the alert was posted is launched with an ALP_APP_DISPLAY ("--alp-display") launch code. The launch code is accompanied by a single parameter: the value of the handle argument to the original alert posting call.
Button Labels

Through the use of various properties, you can alter the labels on these buttons, change the actions that occur when the buttons are clicked, remove buttons, and even add a fourth button to the button bar.

button-1
If supplied, specifies the label to be used for the first button (by default, labeled "OK"). If this button's label is set to the empty string (""), the button is removed from the button bar.
button-2
If supplied, specifies the label to be used for the second button (by default, labeled "Snooze"). If this button's label is set to the empty string (""), the button is removed from the button bar.
button-3
If supplied, specifies the label to be used for the third button (by default, labeled "Go To"). If this button's label is set to the empty string (""), the button is removed from the button bar.
button-4
If supplied, causes the fourth button to be displayed with the specified label. By default, this button is not displayed.
Button Actions

Specify button actions similarly, using keys from "action-1" through "action-4" (where action-1 specifies the action for button 1, action-2 specifies the action for button 2, etc.). The value of each of these keys specifies the desired action:

accept
Unless you have defined the action-accept property, this action does nothing. Alternative behavior may be supplied by defining the action-accept property to any of the other actions in this list, like this:

AlpAttnProp p9 = {"action-accept", "dismiss"}; 

clear
Unless you have defined the action-clear property, this action clears the item from the alert database and dismisses the alert item dialog. Alternative behavior may be supplied by defining the action-clear property to any of the other actions in this list, like this:

AlpAttnProp p9 = {"action-clear", "launch bar:com.access.apps.alarmtest 
--myClearLaunchCode"}; 

connect
Sends a message to a specified IPC channel. The first argument following the connect keyword specifies the channel. Any remaining arguments comprise the final part of the message, after the first three parameters that were passed to the alp_attn_alert_post... call.
connect-clear
Clears the alert from the alert database, then performs the same action as connect.
def-connect
Sends a message to the default IPC channel (alter the default channel by defining the callback-channel property). Any arguments following the def-connect keyword comprise the final part of the message, after the first three parameters that were passed to the alp_attn_alert_post... call.

AlpAttnProp p9 = {"action-4", "def-connect reject"}; 

def-connect-clear
Clears the alert from the alert database, then performs the same action as def-connect.
delete
Delete the alert item. This removes the alert item from the Attention Manager's database.
dismiss
Dismisses the alert item dialog. The alert itself remains in the Attention Manager's database and can be accessed through the list dialog.
display
The alert item dialog is dismissed, the item is cleared from the alert database, and the application specified when the alert was posted is launched with an ALP_APP_DISPLAY ("--alp-display") launch code. The launch code is accompanied by a single parameter: the value of the handle argument to the original alert posting call.
exchange
Processes an Exchange Manager request, then dismisses the alert item dialog and clears the item from the alert database. The format for this command is:

exchange verb data_type [url=url] [(int|str)/param=value]... 

goto
Unless you have defined the action-goto property, this action is identical to the display action. Alternative behavior may be supplied by defining the action-goto property to any of the other actions in this list, like this:

AlpAttnProp p9 = {"action-goto", "launch bar:com.access.apps.alarmtest 
--alp-primary --alp-display 1"}; 

launch
Launches a specified application without clearing the alert. Following the word launch, separated by a space, should be the full name of the package containing the application to be launched. After that, if appropriate, should be launch codes and any needed parameters to be passed to the application, each separated by spaces. For example:

AlpAttnProp p9 = {"action-4", "launch bar:com.access.apps.alarmtest 
--alp-primary"}; 

launch-clear
Clears the alert from the alert database, then performs the same action as launch.
okay
Unless you have defined the action-okay property, this action is identical to the dismiss action. Alternative behavior may be supplied by defining the action-okay property to any of the other actions in this list, like this:

AlpAttnProp p9 = {"action-okay", "launch bar:com.access.apps.alarmtest 
--alp-primary --alp-display 1"}; 

reject
Unless you have defined the action-reject property, this action does nothing. Alternative behavior may be supplied by defining the action-reject property to any of the other actions in this list, like this:

AlpAttnProp p9 = {"action-reject", "dismiss"}; 

snooze
Dismisses the alert item dialog for some period of time, after which it is redisplayed. By default, this period is 5 minutes. To specify a different period, specify an integral number of minutes after the action, like this:

AlpAttnProp p2b = {"action-2", "snooze 2"}; 

Physical Buttons

The Attention Manager ties the "off hook" key to the action-accept action. By default, this action does nothing. See the description of the accept action, above, for instructions on specifying behavior for this action.

Similarly, it ties the "on hook" key to the action-reject action. By default, this action does nothing. See the description of the reject action, above, for instructions on specifying behavior for this action.

Alert Sounds

When an alert is first presented to the user, a default tone is played. As with just about every other aspect of the alert, you can customize the sound that is played through a set of properties. This allows you to indicate the alert type to the user by sound, so that, for instance, an incoming SMS message can be distinguished from a Calendar alarm simply by sound.

alert-tone-url
Specify an MP3 file (or any file format that can be played using a Media Session) containing the sound to be played. By default, the sound is played once; provide a value for the alert-tone-repeat-count key to have the sound played multiple times.
ring-tone-url
Specify an MP3 file (or any file format that can be played using a Media Session) containing the sound to be played. The sound is played continuously unless a value for the alert-tone-repeat-count key is provided; if so, the sound is played the specified number of times.
alert-tone-repeat-count
A numeric value (as a string) indicating the number of times to play the alert tone or ring tone. If this property is not specified, an alert tone is played once while a ring tone is played continuously.
alert-tone-max-duration
The maximum amount of time (in seconds, specified as a string) to play the alert or ring tone. Note that this duration is a total amount of time; if the MP3 file is 10 seconds long, and the alert-tone-repeat-count is set to 5, but the alert-tone-max-duration is set to 30 seconds, the alert or ring tone will only be played three times.

The ring tone takes precedence over the alert tone; if you specify both, the ring tone will be played. If you specify the empty string ("") for the ring tone, no sound will play. If you specify neither value, a default sound will be played.

Nagging

When the alert item dialog is displayed, the alert sound is played as specified using the above properties. In some situations it might be useful to play the alert sound at periodic intervals until the user acknowledges the alert. This is done through the use of the "nag" properties:

nag-interval
The length of time between nags, in seconds. A value of zero prevents nagging from occurring (the alert sound is only played when the dialog is first displayed). There is also a minimum interval (currently, 30 seconds); if you specify an interval value greater than zero but less than this interval, the minimum interval value will be used.
nag-repeat-count
The number of times the user should be nagged. If you do not specify a value for this property, or you specify a value less than zero, the nagging will be repeated indefinitely. Specify a number greater than zero to have the nagging occur for a set number of times.

Nagging does not occur unless you explicitly request it by setting the nag-interval property.

Note that the nagging behavior only occurs while the alert item dialog is displayed; it does not occur if the list dialog is displayed, and if the user acknowledges the alert item in any way, the nagging ceases. If you want the alert to be brought back to the user's attention after they have dismissed the dialog, see the snooze property.

Further Alert Item Dialog Customization

If all of the above is not enough, you can actually supply your own template for the alert item dialog. The template you provide should be a .glade file. Text areas that you wish to define when the alert is posted should be GtkLabel objects, and should have names corresponding to the text properties detailed in "The Text Areas." For instance, "markup-1" or "text-2". Similarly, buttons should be GtkButton objects and should be named "button-1" through "button-4" (your template can have from zero to four of these redefinable buttons).

To let the Attention Manager know that you wish to use your template for a particular alert, you need to define yet another property: the "dialog-url" property. For instance:


AlpAttnProp p0 = {"dialog-url", "bar:com.access.apps.alarmtest/myTemplate.glade"}; 

Posting the Alert

Once you have set up the properties array using a set of key-value pairs, posting the alert is fairly simple. As shown in the sample code in Listing 10.2, call alp_attn_alert_post() (or alp_attn_alert_post_with_callback(), if the alert is being posted from a service daemon that can't be launched like a conventional application), supplying your properties array along with the other required parameters.


			err = alp_attn_alert_post( 
				"bar:com.access.apps.alarmtest", 
				"alp/alarmtest/alarm", 
				"aUniqueString", 
				NULL,			// interface file URL (none) 
				ALP_ATTN_PRIORITY_NOTICE,   // priority 
				0,				// no timeout 
				nprops,		 
				properties		 
				); 

Most of the parameters should be fairly easy to work out, but a couple warrant further explanation.

Events are uniquely identified through the tuple of bundle ID, event name, and event handle. The bundle ID identifies the application to be launched in the event the user taps the Go To button, and is typically of the form bar:com.access.apps.calendar. The event name is an application-defined string that names the event type, while the event handle is an application-defined string intended to help the application associate the alert with the actual event's data. For example, the event handle might be a phone number or the locally-unique ID (LUID) of a calendar record.

The alert priority is an integer value3, and is used to specify how "intrusive" the alert should be. How the various priorities affect the alert are listed in Table 10.1.

Table 10.1  Alert priorities 

Priority

Effect

ALP_ATTN_PRIORITY_CRITICAL (or ALP_ATTN_PRIORITY_MAX)

The user is presented with an alert item dialog only. If the device is equipped with a secondary display, it is displayed there as well. The alert never appears in the list dialog, and does not survive a reboot.

ALP_ATTN_PRIORITY_IMPORTANT (or ALP_ATTN_PRIORITY_HIGH)

The user is initially presented with an alert item dialog. The alert also appears in the list dialog, and on a secondary display if one is present.

ALP_ATTN_PRIORITY_NOTICE
(or ALP_ATTN_PRIORITY_MEDIUM or ALP_ATTN_PRIORITY_DEFAULT)

The user is initially presented with an alert item dialog. The alert also appears in the list dialog, and on a secondary display if one is present.

ALP_ATTN_PRIORITY_LIST

The alert only appears in the list dialog. An alert item dialog is not displayed for the alert, and if the device has a secondary display, the alert is not displayed there.

ALP_ATTN_PRIORITY_SOUND (or ALP_ATTN_PRIORITY_LOW or ALP_ATTN_PRIORITY_MIN)

The user is notified of the item by sound and/or vibrate mechanism only. An alert item dialog is not displayed, nor is the item added to the list dialog.

Regardless of the priority, when the alert is raised to the user a sound is played (unless you have used the properties array to specify that no sound should be played) and, if the device is so equipped, the device vibrates.

The duration argument allows you to specify how long an alert remains active. Once the specified duration has been exceeded, the Attention Manager is free to remove that alert. Normally you provide a value of zero for the duration, indicating that the event should remain until it is dismissed by the user.

Handling a "Go To" Request

When the user clicks the Go To button in the alert item dialog, or when the user taps an alert item in the list dialog, the application specified when the launch was posted is launched (or relaunched, if it is already running) as the primary application. In addition to the ALP_APP_PRIMARY launch code, the application receives the ALP_APP_DISPLAY launch code—which is immediately followed by the event handle supplied when the alert was posted.

Because of the ALP_APP_PRIMARY launch code, you can be confident that your application can safely display its UI. The event handle indicates the event associated with the alert: typically, your application should retrieve the data associated with the event and present it to the user. For instance, when the user is notified of an appointment, clicking Go To tells the Calendar application to display the day containing that particular appointment. At this point the application is fully running, so that the user can examine details of the appointment, cancel or reschedule it, or create new calendar events as they choose.

Exploring the Attention Manager

In the Simulator or on an ACCESS Linux Platform device to which you are connected using telnet you can use the alp_alert command to exercise the Attention Manager. You can use it to post alerts, display the contents of the alert database, remove all alerts from the database, and the like. The full syntax of the command can be obtained by using the "help" option:

alp_alert --help

The command is also fully documented, with examples, in the "Command-Line Tools" chapter of the Development Tools book.

Preferences and Global Settings ^TOP^

The Global Settings Service, part of the Hiker Application Framework, provides a common API by which applications and services can store and retrieve device settings and application preferences. Settings and preferences are organized as if they were in a simple filesystem, with keys organized along the lines of a directory hierarchy. Each key either has a value or is a directory containing more keys. For example, the key /alp/themes/current-theme contains a string identifying the current UI theme. The keys /alp and /alp/themes are directories.

In addition to a value, as in a filesystem keys have a user ID (UID), a group ID (GID) and a set of access permissions.

The Global Settings Service is made up of both a server and a client library. The client library is linked with the client application and handles all communication with the server. The server is architected so that it can use any of a number of underlying mechanisms. For ACCESS Linux Platform, the underlying mechanism is SQLite: the tree hierarchy is actually implemented using relational tables through SQL4. Because SQLite provides no effective access control, the server uses Unix file access control on the database file to prevent access by others. The server also keeps track of which users and groups are allowed to access certain keys, enforcing access control.


NOTE: This section details how to manipulate keys and values programmatically. To aid in developing and testing your application, ACCESS Linux Platform provides the alp_settings command which allows you to explore and manipulate global settings from the command line. See "Exploring and Manipulating Global Settings" for instructions on using this command.

Keys ^TOP^

The Global Settings Service imposes no structure on keys: they are simply C strings. By convention, however, ACCESS Linux Platform uses keys that are structured along the lines of POSIX file paths.

The possible key names or key strings form a "key space." The key strings, or paths, can be absolute or relative. Absolute key paths must start with "/". Relative key paths are relative to a "current directory" (or "cwd"); use alp_settings_set_cwd() to set the current directory.

A "directory" is a key which contains other keys. To keep things simple, directory keys cannot have their own content. If you add a child key to an existing key which has no value, the existing key becomes a directory; subsequent attempts to set the content for that key will fail. Permissions for directories follow the corresponding semantics in the Unix file system.

A directory key can be created in any of the following ways:

  • Create a new key that is a child of an existing key. The existing key becomes a directory.
  • Create a key as a directory using alp_global_setting_set_key_directory().
  • Create a key using a string that names one or more directories that have yet to be created. For example, if the key string is "/a/b/c" and neither "/a" nor "/a/b" exist, both will be created when the key is created.

You can create a key with no value: simply initialize an AlpGlobalSettingsValue structure using GLOBAL_SETTINGS_VALUE_INITIALIZER and then use alp_settings_set() to create the key using this value (Listing 10.3 shows this in operation). The key will be created with the type "INVALID" and a size of zero. You can then set its value at a later point in time or you can create another key that turns this one into a directory.

Listing 10.3  Creating an empty key


AlpGlobalSettingsValue emptyValue =
	GLOBAL_SETTINGS_VALUE_INITIALIZER; 
þ   . 
    . 
    .þþþþþþ 
// create the empty key (named "a", relative to the cwd) 
alp_settings_set(context, 	"a", 	&emptyValue); 
þþþþþþþ 
// this turns "a" into a directory, creating key "a/b" 
status = alp_settings_set_key_string_value(context,
	"a/b", "testing 1...2...3"); 

Key Naming Conventions

Key names should follow a standard convention to allow grouping of similar attributes under the same part of the key hierarchy. ACCESS Linux Platform follows the GConf conventions (see http://www.gnome.org/projects/gconf/) as closely as possible for application preferences and system settings, with consideration for device-specific standards such as OMA DM.

Some typical top-level keys are represented here:

/alp
Operating-system settings. For instance, /alp/themes/current-theme.
/dev
Device settings. For instance, the global setting that indicates whether or not the device has a keyboard is "/dev/key-input". For the Simulator, the value for this key is "qwerty". For a device that has a numeric keypad but not a full keyboard, the value for this key is "keypad".
/bundles
Application-specific preferences. The Calendar application, for instance, saves the user's preferred starting time in the day view using this key: "/bundles/bar:com.access.apps.calendar/unsaved/pref_day_start_time". See Chapter 10, "Application Preferences," for specifics on keys used to hold application preferences.
/dm
OMA DM.
/capabilities
Capabilities such as "is Java installed?" or "is Garnet VM installed?"

Global Settings Keys and Access Rights

If you want them to be shared, make sure that the tree and keys that you create have the correct access rights. First, get the global context with alp_settings_open(). Then use alp_settings_set_default_mode() to set a default "access rights creation mode" for all your new keys. For example, if your default mode is to allow group read and execute access on your keys, but only the user who created them can write them (that is, mode -rwxr-x—-), then use alp_settings_set_default_mode(), as shown here:


alp_settings_set_default_mode(
   theContext, 
   S_IRWXU | S_IRGRP | S_IXGRP). 

Access modes can be found in <sys/stat.h>.

To set a specific access right on a specific key (for instance, so that only the user that created it may access it), call alp_settings_set_mode(), like this:


alp_settings_set_mode(
   theContext, 
   aKey, 
   S_IRWXU) 

In general, you should set your default mode to the most permissive one, and restrict only specific keys.

The semantics of permissions (modes) for newly-created keys are the same as semantics for newly-created POSIX files. Here's a couple of examples:

  • Say you create a key several directories deep, such as a/b/c/d, with every component newly-created. Then a, b, c, and d are all created with the current default mode. If you then change the mode of a/b/c/d, only d's mode will change: all of the interior nodes retain their original mode. Thus if you have restricted access to "user only" by setting a default mode of S_IRWXU and you create /x/y/string_value, none of the components (x, y, or string_value) will be group-accessible. If you then change /x/y/string_value to be S_IRWXU | S_IRGRP | S_IXGRP, x and y are not affected. These earlier components will remain S_IRWXU only, and so prevent this path from being read by the group.
  • If you create a/b/c with a particular default mode, then change the default mode and create a/b/c/d, only d gets the new default mode. There is no inheriting of existing parent permissions.

Values ^TOP^

Values are represented by the AlpGlobalSettingsValue data structure. Use the alp_settings_value_get_type() and alp_settings_value_set_type() functions to translate between the native C data types and this data structure. Or, use the alp_settings_set_key_type_value() and alp_settings_get_key_type_value() convenience functions to get and set values without having to explicitly create this data structure.

The following data types are supported for values that can be associated with a Global Settings key:

  • int (an integer)
  • float (a double, in C)
  • string (a NULL-terminated string in UTF8)
  • bool (the boolean values TRUE and FALSE)
  • blob (a binary buffer with a length)
  • dir (a directory in the key space)
  • list (a list of int, float, string, or bool)

The Global Settings client library (see global_settings.h) exposes convenience APIs for reading and writing each of basic data types—but not for lists.

Due to the complexity in creating and manipulating list values, the Global Settings client library doesn't expose specific APIs for manipulating list values. Instead, you have to create lists yourself (and other structures; you can go beyond simple lists, creating lists of lists and other such constructs). Use the glib data structure GList to represent Global Settings list values, and represent each element with a GList node whose "data" member points to an AlpGlobalSettingsValue. Then, use alp_settings_value_set_list() to store the list in the Global Settings database.

Default Values

The Global Settings Service uses a simple mechanism to support default values: for a given key S, it has a default value if another key named S.default exists. If the key S does not exist, but the key S.default does, a "get key" operation will return the value of S.default.

Data Security ^TOP^

The Global Settings Service is not intended to hold security-sensitive data, such as passwords or private keys. Global Settings data is only protected by Unix file permissions; it is not encrypted and has no form of Digital Rights Management (DRM).

The Global Settings Service implements the POSIX file permission model (user, group, and others; readable, writable, or executable) on its keys and key hierarchy. In order to access a key, a run-time process must acquire the appropriate user or group IDs in much the same way it does when accessing a file in the host file system.

For a directory key, the x ("execute") bit controls whether or not the contents of the directory can be accessed. For non-directory keys, the x bits have no meaning and are ignored. As well, the SUID, SGID, and sticky bits are not honored and are ignored for Global Settings.

The Global Settings Service imparts no meanings to the UIDs and GIDs, except for UID 0. UID 0 represents "root" and has permissions for everything.

The UID and GIDs for keys are modifiable according to the following rules:

  • The UID cannot be changed except by root.
  • The GID cannot be changed except by root or by the owner.

IMPORTANT: The root directory is owned by root: when security is enabled, only the root user can create keys in the root directory. If you need a new private hierarchy not nested under one of the standard directories (such as /alp/bundles or /alp)—something like /x/y/z, say—you must explicitly put /x and /x/y in the default XML files (see "Specifying UID, GID, and Mode"). Be sure to specify the proper mode and group ownership for the directories in the Default Settings XML file.

You use the following functions to alter the permission settings for a key (comparable functions are used to get the settings):

  • alp_settings_set_uid()
  • alp_settings_set_gid()
  • alp_settings_set_mode()

Change Notification ^TOP^

Clients that wish to be informed of changes made in global settings can register with the Notification Manager. The Global Settings daemon sends to Notification Manager a string representing each key that it changes. Notification Manager then notifies each application that has registered for notification of changes to that key or to a prefix of that key. For instance, an application that registered for "oma/apps" would be notified when any of the following keys changed: "oma/apps/calendar/fontsize", "oma/apps/date/background-image", or "oma/apps".

Following the conventions of the Notification Manager, each key change notification has a notification type composed of "/alp/globalsettings/keychange/" (ALP_NOTIFY_EVENT_GLOBAL_SETTINGS_KEY_CHANGE) plus the key string. For example, a change to the key "oma/apps/calendar/fontsize" causes a notification to be broadcast with the notification type "/alp/globalsettings/keychange/oma/apps/calendar/fontsize".

System Preferences ^TOP^

Many system-level preferences are stored using the Global Settings Service and can be retrieved and possibly altered (depending upon whether or not special permissions are required) using the global settings APIs. The header file alppreferences.h contains keys for many of these system-wide preferences.

Reading and writing these values is generally a simple matter. For instance, Listing 10.4 shows how to set the device's master volume level to the value specified by the volume variable.

Listing 10.4  Setting the master volume level


#include <alp/global_settings.h> 
 
AlpGlobalSettingsContext *gContext; 
	 
alp_settings_init(); 
alp_settings_open(&gContext); 
 
if(volume == 0) { 
	alp_settings_set_key_bool_value(gContext,
		ALP_PREFS_VOLUME_SILENTMODE, TRUE); 
} else { 
	alp_settings_set_key_bool_value(gContext,
		ALP_PREFS_VOLUME_SILENTMODE, FALSE); 
} 
alp_settings_set_key_int_value(gContext,
	ALP_PREFS_VOLUME_MASTERVOLUME, volume); 
 
alp_settings_close(gContext); 

Getting the current volume setting is simply a matter of retrieving the value for the master volume key (the Global Settings Service must have been initialized and opened prior to making this call, as is done in Listing 10.4):


status = alp_settings_get_key_int_value(gContext,
    ALP_PREFS_VOLUME_MASTERVOLUME, &volume); 

Application Preferences ^TOP^

Application preferences should be stored using the Global Settings Service. Keys should follow the form:

/bundles/bundle_name/[saved|unsaved]/more_keys

For instance:

/bundles/bar:com.access.apps.calendar/unsaved/
pref_day_start_time

Application preferences are divided into two groups: "saved" and "unsaved." Saved preferences are those that will remain even after their associated application is deleted. For example, a game might put the number of demonstration days remaining in the saved preferences, so that even if the user deleted and then reinstalled the application, the demonstration days remaining would be preserved. Unsaved preferences, on the other hand, are deleted when the associated application is uninstalled.

Writing Application Preferences

Listing 10.5 shows how to write an application preference using the Global Settings Service. Note that you need to initialize and open the Global Settings system prior to performing any other operations. When you are done, close it with alp_settings_close().

This example illustrates a popular technique for constructing the key strings. Because the first part of the string is largely fixed ("bundles/", followed by your application's bundle name) you can define a constant for the unchanging portion as the example does with UNSAVED_KEY_PREFIX (this constant also includes the "/unsaved/" portion of the key as well). A second constant defines the portion that varies from one unsaved key to another (here, MY_KEY). Then, when specifying the key you can use this construct:

UNSAVED_KEY_PREFIX MY_KEY

This example sets the key "/bundles/bar:com.access.apps.test/unsaved/test-key" to the string value "testing 1...2...3".

Listing 10.5  Writing application preferences


#include "hiker/global_settings.h" 
 
#define UNSAVED_KEY_PREFIX \
	"/bundles/bar:com.access.apps.test/unsaved/" 
#define MY_KEY "test-key"þþþþ  
   . 
   . 
   . 
AlpGlobalSettingsContext *context; 
alp_status_t status; 
 
alp_settings_init(); 
alp_settings_open(&context); 
 
status = alp_settings_set_key_string_value(context,
					UNSAVED_KEY_PREFIX MY_KEY,
					"testing 1...2...3"); 
if(status == ALP_STATUS_OK){ 
	g_print("Key saved OK\n"); 
} else { 
	g_print("Problem saving key\n"); 
} 
 
alp_settings_close(context); 

Reading Application Preferences

The code in Listing 10.6 performs the reverse of the code in Listing 10.5, reading back the value of the key "/bundles/bar:com.access.apps.test/unsaved/test-key". Note that you need to provide an adequately sized space into which the returned string can be copied.

Listing 10.6  Reading application preferences


#include "hiker/global_settings.h" 
 
#define UNSAVED_KEY_PREFIX \
	"/bundles/bar:com.access.apps.test/unsaved/"þþ 
#define MY_KEY "test-key"þþ  
   . 
   . 
   . 
AlpGlobalSettingsContext *context; 
alp_status_t status; 
char buffer[20]; 
þþþþþþþ 
alp_settings_init(); 
alp_settings_open(&context); 
þþþþþþþ 
status = alp_settings_get_key_string_value(context,
					UNSAVED_KEY_PREFIX MY_KEY,
					buffer,
					sizeof(buffer)/sizeof(char)); 
if(status == ALP_STATUS_OK){ 
	g_print("Value: %s\n", buffer); 
} else { 
	g_print("Error retrieving value for key\n"); 
} 
 
alp_settings_close(context); 

Setting Ownership and Permissions

The example shown in Listing 10.7 illustrates the creation of a key hierarchy with proper user and group IDs and permissions:

Listing 10.7  Setting UID, GID, and mode (permissions)


#include "alp/global_settings.h" 
 
/* assuming the group and user ids are predetermined */ 
 
#define TELEPHONY_OWNER  31 
#define TELEPHONY_GROUP  31  
 
    AlpGlobalSettingsContext *context; 
    
    const char *key = "/dm"; 
     
   
     
    alp_settings_init(); 
    alp_settings_open(&context); 
     
    alp_settings_begin_transaction(&context); 
     
    alp_settings_set_default_mode(context, S_IRUSR | S_IWUSR | S_IXUSR); 
     
    alp_settings_set_key_directory(context, key);   /* required for /dm to be a 
directory with child keys later */ 
    alp_settings_set_uid(context, key, (uid_t) TELEPHONY_OWNER);  
    alp_settings_set_gid(context, key, (gid_t) TELEPHONY_GROUP);  
     
    alp_settings_complete_transaction(context); 
     
    . 
    . 
    . 
/* later */ 
 
    alp_settings_set_key_string_value(context, "/dm/owner", "John Doe"); 

Initial Key Installation ^TOP^

With the above convention for keys, it becomes desirable for the applications or specific device (drivers) to create its key hierarchy during the installation time (or at the point of system image creation). Once created a hierarchy may be protected from access by applications other than the designated applications.

The initial key installation follow these steps:

  • A group ID is pre-assigned to a particular application (group). This assignment is to be guaranteed at run time by the Bundle Manager and system security.
  • After an application is installed the Bundle Manager copies the key data from the Default Settings XML file into the Global Settings database and properly set the user and the group ids and the permissions for these keys.

Default Settings XML File

The XML file that contains the initial keys and their values follows a particular format:


<root> 
	<keyvalue> 
		key-specific data... 
	</keyvalue> 
	<keyvalue> 
		key-specific data... 
	</keyvalue> 
   . 
   . 
   . 
</root> 

The key-specific data must have the following three child elements:


<key>...</key> 
<type>...</type> 
<value>...</value> 

The <key> element contains the name of the key. The contents of the <type> element can be one of the canonical type names discussed in "Values." The contents of the <value> node depends upon the type; Table 10.2 shows examples of each for the supported data types. Note that for blob keys you can specify the value either as an integer, as a string, or as an array of bytes specified in hexadecimal.

Table 10.2  Examples of <value> elements 

Type

<value> Examples

int

<value>123</value>

float

<value>123.456</value>

string

<value>a string</value>

bool

<value>TRUE</value>
  or
<value>FALSE</value>

blob

<value type="int">12</value>
  or
<value type="string">a string</value>
  or
<value type="hex">00 01 02 03 </value>

Specifying UID, GID, and Mode

The Default Settings XML file supports the specification of mode (permissions), UID, and GID for keys, as well as file-wide defaults for each. Listing 10.8 illustrates these specifications.

Listing 10.8  Default Settings XML file showing UID, GID, and mode, plus defaults


<root>                                                                                  
    <default_mode>0333</default_mode> 
    <default_gid>wheel</default_gid> 
    <default_uid>me</default_uid> 
    <keyvalue>                                                                          
        <key>/a</key>                                                                    
        <type>string</type>                                                             
        <value>aaaaaaaaaaa</value>                                                      
    </keyvalue>                                                                         
    <keyvalue>                                                                          
        <key>/b/b/b/b/b</key>                                                           
        <type>string</type>                                                             
        <value>bbbbbbbbbbb bbbbbbbb</value>                                             
        <uid>global_settings</uid> 
        <gid>trusted</gid> 
    </keyvalue>                                                                         
</root> 

The optional <default_mode>, <default_gid>, and <default_uid> elements establish attributes to be applied to those keys without explicitly specified attributes of their own. The default elements must be placed before any <keyvalue> elements. If default values are not given, the following will be used:

Default UID
root
Default GID
root
Default mode
0700

As shown in Listing 10.8, a given key can have its own specific attributes different from the default. In the example, the key "/a" inherits the default mode, UID, and GID. The key "/b/b/b/b/b", on the other hand, inherits the default mode but has its own values for UID and GID.

When specifying the UID and GID, you cannot use numerical values but instead must specify user and group names. This is because the numerical values are determined by the Security Framework, not the Global Settings Service. The Global Settings Service will adjust the key to the correct numeric UID or GID when the Global Settings database is initialized.

Overriding the GID and/or UID is useful for system settings that need to belong to specific groups or users. It is also useful for settings that are controlled through preference panels. Since bundles and preference panels are different users in an ACCESS Linux Platform system, and since the preference panels run under the group "trusted", it is useful to make such keys with the group "trusted" and world-readable but group-writable.


NOTE: Overriding the UID and/or GID is less useful for application bundle keys, since bundles do not have their own user name.

For keys within directories, you need to specify settings for each directory level. Otherwise, the directory will have the system default UID, GID and mode, which may not be what you want. Listing 10.9 illustrates how you could establish the settings for each level of the key "/a/b/c".

Listing 10.9  Applying settings to each level of a directory


<keyvalue>                                                                          
        <key>/a</key>                                                           
        <type>dir</type>    
        <uid>global_settings</uid> 
        <gid>trusted</gid> 
</keyvalue>  
<keyvalue>                                                                          
        <key>/a/b</key>                                                           
        <type>dir</type>    
        <uid>global_settings</uid> 
        <gid>trusted</gid> 
</keyvalue>  <keyvalue>                                                                          
        <key>/a/b/c</key>                                                           
        <type>string</type>    
        <value>abc</value> 
        <uid>global_settings</uid> 
        <gid>trusted</gid> 
</keyvalue>  


IMPORTANT: The UID, GID, and mode for top-level bundle directories ("/bundles/bundle_name") are set by the Bundle Manager. Attempts to set these attributes on top-level bundle directories in a Default Settings XML file have no effect and are ignored.

Data Backup ^TOP^

As the data residing in a mobile device, the Global Settings database is a permanent part of the user environment that should be backed up to a host computer. The Global Settings database contains both secure and insecure items. The backup mechanism only stores the insecure settings onto the host machine.

Transaction Support ^TOP^

The Global Settings Service supports transactions. The entire database is locked for writes; thus, there can be only one process writing to the Global Settings database at one time.

Keys can be set either in transaction or non-transaction modes. If alp_settings_begin_transaction() is called, then all subsequent changes will not take place until the following alp_settings_complete_transaction() call. If any of the alp_settings_set... functions are called outside of a transaction, each change is committed immediately.

Because the Global Settings database could be locked indefinitely by a faulty client which opens a transaction and then never closes it, the Global Settings Service breaks a transaction automatically if the client does not perform a Global Settings database operation for a period of five seconds. Generally, transactions should be as brief as possible: any application doing updates should carry out updates in batches within a minimal interval. Applications that violate the constraint thus have no guarantees that their transaction semantics will be fully carried out and are own their own with respect to their data integrity.

Preference Panels ^TOP^

The ACCESS Linux Platform Preferences system lets the user view and interact with system-wide preference panels. It is comprised of several main parts:

  • A framework that lets applications and other processes launch preference panels, switch between preference panels, and instantiate UI widgets to let the user select preference panels to work with.
  • A launcher application that uses the framework to let the user select among panels available on the device.
  • A sample preference panel that developers can use as a basis for developing new panels.

The system provides the following additional features:

  • Any application can launch a panel, as can other executables such as status gadgets or other panels.
  • Preference panels are transient applications, so when a panel is launched, the active "normal" application stays in the background, and reappears when the panel is dismissed.
  • If a preference panel is open, and the main application exits, the preference panel also exits.
  • Each preference panel is launched in a separate process, enhancing system robustness.
  • Users can switch from one panel to another using a combo box in the panels.
  • Only one panel is active at a time—if a panel is showing, and another is launched, the framework closes the first one.

The Preferences system does not manage preference data. It is merely a system for managing the preference panels themselves. It is up to the preference panels, and related applications and system services, to manage their own preference data using the Global Settings Service or some other mechanism.

Preferences Framework

The Preferences Framework is a .so file that provides several features that applications can use to work with preference panels:

  • Initialization and termination routines.
  • A GTK+ combo box that lets the user select a preference panel to work with.
  • An API call to display a specific preference panel.

The Preferences Framework also has a private API for generating a scrolling list of preference panels that is used in the Preferences Launcher application.

Panel List

The Panel List lists all of the preference panels, with each preference panel represented by an icon, a name, and a brief description.

Applications should normally use the Panel List to let the user pick a panel to work with. When the user picks an item from the list, the corresponding panel will appear in front of the current application, and replace any currently-showing preference panel. Everything needed to launch the panel is encapsulated in the Panel List widget, which internally uses the alp_prefpnl_launch() API to do the launch.

Panel Combo Box

The Panel Combo Box is simply a combo box that lists each preference panel by name.

Preference panels should normally use the Panel Combo Box to let the user pick a panel to work with. When the user picks an item from the combo box, the corresponding panel will appear in front of the current application, and replace any currently-showing preference panel. Everything needed to launch the panel is encapsulated in the Panel Combo Box widget, which internally uses the alp_prefpnl_launch() function to do the launch.

Initialization and Termination

The Preferences Framework provides an initialization call, alp_prefpnl_init(), that must be called before creating a Panel List or Panel Combo Box, but after initializing GTK+ (and libglade, if the executable uses that). Among other things, this call determines which preference panels are present in the system by querying the bundle manager for application bundles whose manifests contain the <Prefpanel> tag.

The Preferences Framework also provides a termination call, alp_prefpnl_fini(), that must be called if alp_prefpnl_init() has been called. The termination call does any required cleanup. In debugging builds, if this is not called when needed, the .so will emit a trace warning when it is closed.

Preference Panel Launching

The alp_prefpnl_launch() function lets an application or other executable launch a specified preference panel. When this is invoked, it ordinarily emits a request for any existing preference panel to exit, and then launches the specified panel. However, if the requested panel is already showing, this function call does nothing, but returns ALP_STATUS_OK.

Glade Support

If Glade is linked into the executable using the framework, the framework will register its widgets as Glade custom widgets under the names alp_prefpnl_list and alp_prefpnl_combobox.

Preference Panels as Applications

Preference Panels are transient applications, so when a panel is launched, the active application stays in the background, and reappears when the panel is dismissed.

Each preference panel will normally contain a minimum of three UI widgets: the (localized) word "Preferences" at the upper left, a Panel Combo Box at the upper right showing the name of the current panel, and a "Done" button at the lower left. When the user presses the Done button, or a Cancel button if one exists, the preference panel should exit. This will bring the main running application from background to foreground.

The preference panel can receive an exit request, either when the main application exits, or when alp_prefpnl_launch() tries to launch a different panel. When this happens, the preference panel must save its state as appropriate and exit.