This section describes each of the system services. Very few of the services apply to the News application, so most of the code samples in this section are simple ones to illustrate the service calls and handlers.
The system services are accessed through Mojo.Service.Request()
,
or the equivalent scene controller method this.controller.serviceRequest()
:
Mojo.Service.Request("palm://serviceName", options, requestOptions);
The arguments are described in Chapter 8, but briefly:
serviceName
Is a URI-formatted string that uniquely specifies the service name.
options
Includes the designated method, parameters, and callback functions.
requestOptions
Is either set to true, requesting automatic subscription
renewal on error, or an object with a resubscribe
property and/or a useNativeParser
property.
Be careful to prevent garbage collection of your service requests. Recall from
Chapter 8 that requests made with this.controller.serviceRequest()
are
garbage-collected and destroyed when the scene is popped. You should use
Mojo.Service.Request()
to save the
request object and handle termination of the request yourself to prevent
garbage collection.
Service requests do not complete when you make the call; they are
all asynchronous. If it’s possible
that the life of your request will outlast
the life of the assistant when the call is made, you must use
Mojo.Service.Request()
.
Applications can respond to high-level orientation, and shake events or elect to receive raw accelerometer data through acceleration events. You will use framework controller functions or event listeners to receive accelerometer events.
You can use the orientation events to rotate your application to track the orientation of the device between portrait and landscape modes. Shake events can be applied in creative ways as alternatives for user input, such as start/stop indicators. The raw acceleration data is useful for games or other applications that can integrate device movement directly into the application’s interaction with the user.
It’s extremely simple to have your application respond to changes in screen orientation.
Within your stage assistant or main scene assistant, set your
application’s card stage window to a free orientation through the stage
controller’s setWindowOrientation()
method:
if (this.controller.stageController.setWindowOrientation) { this.controller.stageController.setWindowOrientation("free"); }
The system will rotate the stage window, following the
orientation of the device between up
(normal portrait), right
(clockwise rotation from up), down
(portrait, but the reverse of up), or
left
(counter-clockwise from up).
You can use the same method to force an orientation with any of up,
down, left, or right passed as the argument string.
There is a corresponding getWindowOrientation()
method to retrieve
the current stage window orientation.
If you want to take a specific action in response to a window’s
orientation change, you can add a listener to the stage window for the
orientationchange
event, and then respond to the change:
this.controller.listen(document, "orientationchange", this.handleOrientation.bindAsEventListener(this)); . . . MyAssistant.prototype.handleOrientation(event) Mojo.Log.info("Orientation change position: ", event.position, " pitch: ", event.pitch, " roll: ", event.roll); };
The new orientation data are passed as properties to the event object described in Table 9-1.
Table 9-1. Orientation change event properties
Property | Value | Description |
---|---|---|
| Integer | Numeric value from 0 to 5, where: 0 = face up 1 = face down 2 = up, or normal portrait 3 = down, or reverse portrait 4 = left, or landscape, left side down 5 = right, or landscape, right side down |
| Float | Righthanded rotation about the x-axis (in degrees) |
| Float | Righthanded rotation about the y-axis (in degrees) |
Pitch and roll are absolute values with respect to the device being face up and flat on a table (in this position, they’re guaranteed to be within [−90 degrees, 90 degrees]). Start with the device flat on the table in portrait mode with the bottom of the device facing you; pitch and roll in this position are both 0.
As you tilt the device toward you, pitch changes from 0 to −90 (when the device is completely vertical). Start again with the device flat: tilting the device away from you, the pitch increases from 0 to 90. Tilting to the right increases the roll from 0 to 90. Tilting left changes roll from 0 to −90.
When the device is face up and completely vertical the pitch is −90 degrees. As you continue to tilt the device toward you, the pitch goes to 0 (face down). The same is true of the roll. When you tilt the right side perpendicular with the ground, the roll will be 90. As you continue rotating (so that the device is face down), the roll decreases to 0.
With these definitions, you can see that there are two orientations where you’ll get the same angles (one when face up and one when face down). You can use the z-axis to distinguish the two cases.
There are three events: shakestart
, shaking
, and shakeend
. As you would expect, the start and
end events are sent as soon as a shaking motion starts or stops.
Shaking events are sent regularly while shaking continues, at the same
rate as raw events, or 4Hz. The events include a magnitude
property
(in units of gravitational acceleration or
g’s), where a large value indicates more vigorous
shaking:
this.controller.listen(document, "shaking", this.handleShaking.bindAsEventListener(this)); . . . MyAssistant.prototype.shaking(event) { Mojo.Log.info("Shaking with magnitude: ", event.magnitude); );
In practice, you may not need to listen to the shake start and end events unless you have some specific actions for those events. The shaking events will be sent as soon as the shaking motion commences, and will cease when the motion stops.
Detailed acceleration data is provided with each acceleration
event,
which are sent regularly whenever the device is in motion. While this
information is accurate enough for games and similar dynamic
applications, it won’t be sufficient to allow inertial position
tracking applications. Note that acceleration events are targeted at
the window and do not bubble up to the containing context:
this.controller.listen(document, "acceleration", this.handleAcceleration.bindAsEventListener(this)); . . . MyAssistant.prototype.acceleration(event) { Mojo.Log.info("X: ", event.accelX, "; Y:", event.accelY, "; Z:", event.accelZ, "; time: ", event.time);
Acceleration events contain additional properties, listed in Table 9-2.
You should use the JavaScript window methods setInterval()
or
setTimeout()
to return to your
application after a delay:
var wakeupFunction = function() { Mojo.Log.info("It's a wakeup call!"); }; window.setTimeout(wakeupFunction, 20000);
This is a lightweight delay timer, which executes a function after a specified
period. In this example code, wakeupFunction
is executed after a delay of 20
seconds. This type of alarm will work as long as the device is awake and
where some imprecision is acceptable.
In all other situations, you will want to use the Alarm service, which is based on the device’s real-time clock (RTC). Alarms are intended to wake applications while minimized or maximized, or to drive polling for dashboard applications. Alarms will:
Accurately account for time changes; timeouts are accurately tracked across device sleep states, timezone changes, manual changes to time settings, or other changes that affect the displayed or perceived time on the device.
Wake the device up from sleep; if needed, timers will wake up the device when the alarm fires.
Fire after a specified delay or at a specified date and time.
Make a specific service request when the alarm fires; commonly
it will be an applicationManager
service request to
call the originating application’s handleLaunch
method, but can be any
service call.
A good example of an alarm at work is using the Alarm service to wake an application up periodically:
this.controller.serviceRequest("palm://com.palm.power/timeout", { method: "set", parameters: { key: "com.palm.app.news.update", in: News.feedUpdateInterval, wakeup: "true", uri: "palm://com.palm.applicationManager/open", params: { id: "com.palm.app.news", params: {action: "updateFeed"} } }, onSuccess: this.onSuccessHandler, onFailure: this.onFailureHandler });
This example from News sets a relative alarm according to the
requested News.feedUpdateInterval
and requests that
it wake up the device if it is asleep. When the alarm fires, it will use
the Application Manager to launch News (even if News isn’t running at
the time) with a launch parameter indicating that this is an alarm for
feed updates. You will learn more about handling launch events and using
alarms with background applications in Chapter 10.
Another option is to set an alarm at a specific date and time:
this.controller.serviceRequest("palm://com.palm.power/timeout", { method: "set", parameters: { key: "com.palm.app.news.daily", at: "04-23-2009 03:30:00", wakeup: "true", uri: "palm://com.palm.applicationManager/open", params: { id: "com.palm.app.news", params: {action: "updateFeed"} } }, onSuccess: this.onSuccessHandler, onFailure: this.onFailureHandler });
In this example, News could be set to update the news feeds at
3:30 A.M. GMT. This calendar alarm uses the at
property (in place of the in
property for a relative alarm) to set the
date and time for the alarm. By definition, you must set calendar alarms
for each occurrence; there isn’t a provision at this time for periodic
or recurring alarms.
To clear the alarm, use the clear
method:
this.controller.serviceRequest("palm://com.palm.power/timeout", { method: "clear", parameters: { key: "com.palm.app.news.daily", }, onSuccess: this.onSuccessHandler, onFailure: this.onFailureHandler });
The alarm’s key
property is used to
clear the periodic alarm that was set with that property.
Some additional notes about alarms:
Setting a relative alarm causes the device to wake and fire the alarm at a fixed time in the future. This is independent of time changes on the device by either the user or an external source (such as network time or timezone changes). The maximum period for relative alarms is 24 hours and the minimum is 5 minutes, but calendar alarms do not have a 24-hour time limit.
Alarms are preserved across reboots. If an alarm expires while the device is shut down, the alarm will fire when the device starts up again.
If the alarm service call fails, one retry attempt will be made 30 seconds later.
These alarms are coarse-grained alarms, so don’t expect millisecond or even one-second resolution. At worst, an alarm may fire a few seconds from the intended time.
Because the Alarm service can wake up your application even while the device is asleep, the service is integral to running an application in the background. We’ll use alarms to turn News into a background application in Chapter 10.
Use the Connection Manager’s getStatus
method to get
updates on the device connection status. Some applications need to
manage data access based on the connection state or type of connection
available:
this.controller.serviceRequest("palm://com.palm.connectionmanager", { method: "getStatus", parameters: {subscribe:true}, onSuccess: this.onSuccessHandler, onFailure: this.onFailureHandler });
The response will provide the connection status at the time of the
call. Use the subscription option to register for updates each time the
connection status changes. In every case, the onSuccess
callback is made with a single
response argument, an object describing the connection status. The
return value is set to true and the connection properties are provided
as described in Table 9-3.
Table 9-3. Response properties for connection status
Palm webOS provides basic location services to get one or more location fixes. You can specify fixes by mode. In automatic mode, the system picks the most appropriate mode based upon your accuracy and response time requirements. Specific fix types include assisted GPS and Cell ID with or without WiFi ID.
You can get the current position from the built-in GPS or through Cell ID or WiFi ID, depending on what’s available. The simplest call uses all default settings:
this.controller.serviceRequest("palm://com.palm.location", { method: "getCurrentPosition", parameters: {}, onSuccess: this.locationSuccess.bind(this), onFailure: this.locationFailure.bind(this) });
The default settings will provide a new (not cached), medium accuracy (within 350 meters) fix within 5 to 20 seconds. Through different property settings, you can force greater or less accuracy, faster or slower response times, and agree to accept a cached fix. Typically, greater accuracy means a slower response to the request.
The Location service tries to get a fix with the requested accuracy. If it is not able to get a fix within the maximum allowed time, it returns the best match from the cache. If there is no entry in the cache, the service returns a timeout error.
If the Location service can get a fix, the onSuccess
function
will be called with a response object with the properties described in
Table 9-4.
Table 9-4. Response properties for Location service position fix
Property | Description |
---|---|
| Set to zero if the request is successful; a nonzero value otherwise (see Table 9-5 for a complete list of error codes) |
| The time (in milliseconds) when the location fix was created |
| A number representing the latitude in degrees of the location; valid range is −90.0, 90.0 |
| A number representing the longitude in degrees of the location; valid range is −180.0, 180.0 |
| Horizontal accuracy of the location in meters; if unknown, value is −1 |
| A number representing the compass azimuth in degrees; valid range is 0.0, 360.0; if unknown, value is −1 |
| A number representing velocity in meters per second; if unknown, value is −1 |
| Vertical accuracy of the location in meters; if unknown, value is −1 |
If there’s an error, the onFailure
function is
called with a nonzero errorCode
. A
list of possible errors is provided in Table 9-5.
Use the startTracking
method
to get a series of fixes. Tracking is effective for navigation
applications or anywhere you want to update the device location over a
period of time. A new tracking fix is provided about once a
second:
this.trackingHandle = this.controller.serviceRequest("palm://com.palm.location", { method: "startTracking", parameters:{subscribe: true}, onSuccess: this.trackingSuccess.bind(this), onFailure: this.trackingFailure.bind(this) });
You need to subscribe to this service and save the request object so that you can cancel the tracking request when you are done with it:
this.trackingHandle.cancel();
The onSuccess
function
will be called with a response object that has the same properties
provided with the getCurrentPosition
method; they are described in Table 9-4. As with
getCurrentPosition
, you can get
tracking fixes even without GPS, although at a reduced level of
accuracy (Cell ID or WiFi ID are less accurate than GPS).
If there’s an error, the onFailure
function is
called. You will continue to receive tracking fixes even after an
error, since most errors are transient. In one case though, GPS Permanent Error, the error will persist, but you can
still receive tracking data from Cell ID or WiFi ID.
If you receive a GPS Permanent Error, you can still receive
location services through Cell ID and WiFi ID. In this case, the
error will be reported with a callback to the specified onFailure
function, but thereafter you will receive ongoing tracking fixes
through the onSuccess
callback.
An additional Location service provides you with a physical address when provided with a location described with latitude/longitude values:
this.controller.serviceRequest("palm://com.palm.location", { method: "getReverseLocation", parameters: { latitude: "37.7779", longitude: "-122.414" }, onSuccess: this.reverseSuccess.bind(this), onFailure: this.reverseFailure.bind(this) });
If successful, the onSuccess
callback is
passed a response object with a single address property. The address
is two or three lines, each delimited by semicolons, where the street
address is optional:
street address; locality (eg. in the US, city, state, zipcode); country
For example, this code sample would return the following string:
98th 8th St ;San Francisco, CA 94103 ;USA
The device will automatically go to sleep after a period of inactivity,
where inactivity is primarily defined as the absence of user
interaction: gestures, touches, or keyboard input. The user can set a
preference to trigger sleep after a minimum of 30 seconds or a maximum
of 3 minutes. Some system services, such as audio or video playback will
defer sleep, but if you need to
keep the device awake, you can use the activityStart()
and activityEnd()
methods in the Power Management
service.
You would use these service methods if your application performs an extended operation, such as syncing or downloading a lot of data, or if your application includes a passive viewing feature like a slide show. You will also use this in background applications where you need more than the few seconds allotted during an alarm wakeup.
Alert the Power Management service that you are starting an activity that will require the device to stay awake:
this.controller.serviceRequest("palm://com.palm.power/com/palm/power", { method: "activityStart", parameters: { id: "com.palm.app.news.update-1", duration_ms: '120000' }, onSuccess: this.activitySuccess.bind(this), onFailure: this.activityFailure.bind(this) });
Provide a unique ID, which should be your application ID with an activity name and an occurrence count. This recommended format will allow you to distinguish between requests and manage multiple requests if needed, but the only requirement is that the ID string be unique. The activity’s expected duration is provided in milliseconds and cannot exceed 900,000 milliseconds (15 minutes).
The power management service will automatically terminate your activity request at the end of its duration or 15 minutes, whichever is shorter. You should notify the service when your activity completes, as every bit of power efficiency is important:
this.controller.serviceRequest("palm://com.palm.power/com/palm/power", { method: "activityEnd", parameters: { id: "com.palm.app.news.update-1" }, onSuccess: this.activitySuccess.bind(this), onFailure: this.activityFailure.bind(this) });
The only parameter is the id
provided to the activityStart()
method. Activities are not canceled when an application is closed, so
you should use activityEnd()
in your
cleanup()
method when there are any
outstanding activity requests.
Applications can request a named system property, which is currently limited to
retrieving the unique device ID. Generally, the requested system
property is named as a string to parameter.key
; in this case, the device ID is
named com.palm.properties.nduid
:
this.controller.serviceRequest("palm://com.palm.preferences/systemProperties", { method: "getSysProperty" , parameters:{"key": "com.palm.properties.nduid" }, onSuccess: this.onSuccessHandler } });
The nduid
is a
hardware-encoded device ID that is guaranteed to be unique. An example
device ID is a66690b3632bb592b29c6a15416717b9ee072b3f
.
On a successful callback, you will find the device ID as the value
assigned to the key, com.palm.properties.nduid
. In this
example:
this.onSuccessHandler = function() { Mojo.Log.info("Success; nduid = ", response["com.palm.properties.nduid"]); };
The device ID is the recommended way to uniquely identify the user or their device. The device ID is better than the phone number or device serial number because it is guaranteed to be unique to a specific device, yet it is difficult for others to use it to identify a specific user. The phone number is considered private user information and should never be used or transmitted with any user data. The serial number is printed on the device, making it easier to associate a device to a specific user.
The system is designed to expose a set of services enabling applications to access some general system settings. Currently, the only exposed service is one that provides the system time.
Make the request to the getSystemTime
method,
and optionally subscribe to notifications of changes to the time or
timezone:
this.controller.serviceRequest("palm://com.palm.systemservice/time", { method: "getSystemTime", parameters: {subscribe: true}, onSuccess: this.timeSuccess.bind(this), onFailure: this.timeFailure.bind(this) });
Whether the subscribe property is set or not, the onSuccess
function will
be called initially with the response object as an argument. The properties are
described in Table 9-6.
In most cases, you can use the JavaScript Date object to get the current date and time. The
getSystemTime
service method gives
you the timezone and allows you to subscribe to time changes, which
may be important to your application. In most other cases, the Date
object is a lightweight and more versatile source of date and time
information.
The System Sounds service is used to create audio feedback for direct user actions. This might include button or keypad clicks, transition sounds, action audio, or any audible response to a user action. It’s not intended for sustained audio, such as background audio or any lengthy playback. The call is limited to a static list of sounds and is not customizable.
The specified sounds will be played as soon as the call is
received, with low latency. Call System Sounds using the playFeedback
method,
with the requested sound name as a string assigned to the name
property:
this.controller.serviceRequest("palm://com.palm.audio/systemsounds", { method: "playFeedback", parameters:{name: "shutter", onSuccess: this.onSuccessHandler, onFailure: this.onFailureHandler } });
The callbacks will receive the response object with returnValue
set to true if the sound played
successfully; otherwise, it will be set to false. If false is returned,
the errorText
property will include
an indication of the type of error encountered.
The available sounds are enumerated in Appendix B, and you can find them on the Palm developer site.
18.218.132.6