Hello developer. Login with your existing account. New to Vodafone Developer? Register your account.

+ Login or create an account

Accelerometer Code Notes

Introduction

The Vodadone developer Widget Samples series demonstrates how to use the JIL 1.2.2/WAC 1.0 widget APIs. These notes document the Accelerometer sample widget.

Download the installable widget package and source files here - accelerometer.zip (50.4 KB) .

JIL APIs used

The Accelerometer widget uses the following JIL APIs:

  • Widget.Device.DeviceStateInfo.AccelerometerInfo

JIL Features declared

The Accelerometer widget declares the following JIL features:

Required features:
  • http://jil.org/jil/api/1.1/devicestateinfo
    • Contains properties and methods that provide access to device features that have dynamic state. Properties include hardware status information, for example the current accelerometer axis values, keypad light and backlight status, available memory and CPU utilisation. Methods include callbacks to notify flip, location, and screen size/orientation change events.
  • http://jil.org/jil/api/1.1/accelerometerinfo
    • Contains the device accelerometer X, Y, and Z axis properties.
Optional features:

None.

Compatibility

  • Android: Yes
  • Symbian: No
  • Nokia N8: Yes

Note: At the time of writing the Nokia N8 is the first Symbian device to support the latest version of the Vodafone Apps Manager. On previous Symbian devices the browser runtime is not compatible with the JIL widget format.

Overview

The Accelerometer sample widget demonstrates how to map sensor values from the accelerometer into motion on the screen. In this example, tilting the phone moves a puck over a hockey field.

For each position update, the widget displays the X, Y, and Z axis values and the elapsed time since the last update.

The screenshot below shows the Accelerometer sample widget running on the Sony Ericsson XPERIA X10 mini.

AccelerometerInfo API

Contains the following properties:

  • Number xAxis
    • The value of the accelerometer x-axis which is positive towards the right of the device and negative towards the left.
  • Number yAxis
    • The value of the accelerometer y-axis which is positive towards the top of the device and negative towards the bottom of the device.
  • Number zAxis
    • The value of the accelerometer z-axis which is positive from the screen of the device toward the user and negative behind the device and away from the user.

All values are "doubles" i.e. 64-bit floating-point values (which is JavaScript's standard number representation).

You should always read the xAxis value first, and you should read it every time you want to update values from the accelerometer; querying xAxis triggers the widget runtime to read values for all three axes from the hardware. The values are cached until xAxis is read again. If you don't call xAxis therefore, and until you do call it, you will read stale values from the AccelerometerInfo properties.

Code notes

The widget code demonstrates how to get and display accelerometer readings and use them to animate a page element. The widget uses the X and Y axis readings only, and maps them respectively to the left and top CSS properties of the puck page element:

In accelerometer.js:

var s = app.dom.puck.style;
s.left = (accelInfo.x * -50 / app.GRAVITY) + 50 + "%";
s.top = (accelInfo.y * 50 / app.GRAVITY) + 50 + "%";

where app.GRAVITY is the gravitational constant 9.8.

The result of each calculation is the string representation of the puck element's absolute X and Y position (returned by the API) converted to a percentage of the width (height) of the parent element (i.e. the screen div). The result is set as the left (top) offset style attribute of the element.

The notes below walk through the code in more detail.

  • config.xml

    To use a JIL API, you must declare the corresponding "feature" in your widget's config.xml configuration file.

    Accessing the AccelerometerInfo object requires two features:

    • The AccelerometerInfo feature
    • The DeviceStateInfo feature, representing the parent object through which AccelerometerInfo is accessed

    Because the widget's behaviour depends on these features, in both cases we set the required property to true. (For a feature that was optional, we would set required="false").

    To declare the features, we include the two feature declaration lines shown below in the widget's config.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <widget xmlns="http://www.w3.org/ns/widgets"
      xmlns:jil="http://www.jil.org/ns/widgets1.2"
      height="9999" 
      id="http://reference.vodafone.com/widgets/accelerometer"
      version="0.1.201008310435" 
      viewmodes="fullscreen" 
      width="9999">
      <name>accelerometer
      <description>An example accelerometer widget.
      <!-- FEATURE DECLARATION LINES -->
      <feature name="http://jil.org/jil/api/1.1/devicestateinfo" required="true"/>
      <feature name="http://jil.org/jil/api/1.1/accelerometerinfo" required="true"/>
    </widget>

    If a feature is declared as required the widget runtime will decline to install the widget on a device that does not support the feature.

    accelerometer.html

    The HTML page structure is very simple, and defines:

    • A table for the axis values and time interval reporting
    • A puck – represented by the bullet character entity &bull;
    • A standard footer

    Note that the script is loaded from the bottom of the page.

    Note also that the sample uses the HTML5 doctype declaration (simple DOCTYPE statement). HTML 4.01 and XHTML Basic 1.1 doctypes are both also supported.

    accelerometer.css

    The CSS stylesheet that styles the page is straightforward, declaring styles for the field div. See also the note on Common styles below: non-app specific styles are applied via the class=screen attribute of the field div, and for the puck.

    Two points of interest in the styling of field:

    • The background setting causes the background image to be centred on a black background
      
      background: black url(hockey.png) no-repeat center;
    • The background size
      
      background-size: 100%;
      -o-background-size: 100%;
      -webkit-background-size: 100%;

    You can see from the screen shot of the widget running on the X10 that the background image has been scaled from its 598x800 pixel physical size to fit the X10's 240x320 pixel screen. The combination of background size and centred positioning also have the effect of cropping the image behind other page elements. Changing the center value in the style declaration e.g. to top center will change the origin position, and hence the cropped areas.

    Note that two vendor-specific declarations are also included for the CSS3 background-size property. The -o- and -webkit- prefixes (for Opera and WebKit respectively) indicate that support for this property is still experimental in those runtimes.

    The remaining declarations:

    • Set the colour for the position-info and puck elements
    • Set puck to absolute positioning which is required to be able to reposition it dynamically
    • Set font size and shadow for the puck, and margin for the position-info table.

    accelerometer.js

    All the widget samples use the same template for encapsulating the widget code inside a single app object:

    var app = {
         dom: null, // Will hold references to DOM objects
         /*
           The widget initialization function – called after load.
         */
         init: function() {
              /*
                Setting up references to DOM elements for later usage
              */
              app.dom = {
              // someNode: document.getElementById("someNode")
              }
              // THE INITIALIZATION CODE FOR YOUR APPLICATION GOES HERE
         }
         // Other properties and methods
    };

    This is a basic but important JavaScript pattern. On all platforms, the JavaScript runtime defines only a single global namespace. All globals you define, therefore, are added to the global namespace. Using the above pattern, you define a single global variable, app, and access all your other program functions and ojects as properties of app, to avoid polluting the global namespace.

    Note that app is used for convenience in the samples. While it is not likely to collide with any library name, in your own projects you may want to guarantee that the global name is unique (for example by including your domain URL in the name).

    app then declares:

    • Property GRAVITY, the gravitational constant used in calculations from accelerometer readings
    • Methods init, getAccelerometer, and placePuck

    In JavaScript by the way, a method is just a function that is stored as the property of an object, in this case app.

    Let's look at these methods, starting with the simplest first.

    placePuck()

    This method calculates top and left percentage offsets using the retrieved X and Y values and the GRAVITY constant, and sets the offsets as properties of var s = app.dom.puck.style. The effect is to set the left and top CSS style attributes for the puck element of the widget HTML page. Because puck is declared as having absolute positioning, setting its position style attributes will position it relative to the page.

    getAccelerometer()

    This is the method that makes the JIL API call to read the accelerometer data. Note the use of:

    var accelerometerInfo = Widget.Device.DeviceStateInfo.AccelerometerInfo;

    to bring the accelerometerInfo object into local scope for the three subsequent queries, to avoid chasing repeatedly through the app object's scope chain. This is a common technique which is used repeatedly in the code for the widget samples. See Find out more, below.

    The value returned by getAccelerometer is an object containing x, y, and z properties, which are updated from the values of the JIL accelerometerInfo object, i.e. the following lines of code update and return the current accelerometer values:

    getAccelerometer: function() {
         ...
         return {
              x: accelerometerInfo.xAxis,
              y: accelerometerInfo.yAxis,
              z: accelerometerInfo.zAxis
         }
    }

    init()

    This is the method that is called when the runtime loads accelerometer.html. Note that the load script is placed at the bottom of the page. Again, this is a common technique to enable progressive rendering of the page, since HTML elements are processed and rendered in the order they are encountered by the runtime. See Find out more, below.

    Let's look at the code in detail:

    var dom = app.dom = {
         puck: document.getElementById("puck"),
         indicatorX: document.getElementById("indicator-x"),
         indicatorY: document.getElementById("indicator-y"),
         indicatorZ: document.getElementById("indicator-z"),
         indicatorInterval: document.getElementById("indicator-interval")
    }

    This declares and sets two properties, dom and app.dom, and assigns the same object value to both of them. The assigned object is constructed by the object literal expression, which declares five properties, puck, indicatorX, and so on, and assigns each the value of the relevant JavaScript getElementById DOM method.

    This is another example of making local assignments to collapse the scope chain; accessing indicatorX is more efficient than making the DOM method call document.getElementById("indicator-x"). So in a sense this code is note doing "real" work, it's setting things up efficiently.

    On the other hand, the following code does some work, setting the puck element margins which with these values has the effect of creating an "origin" (i.e. offsetWidth intersection with offsetHeight) at the centre of the puck, assuming a 4 pixel diameter bullet point (see also the CSS font size declarations):

    var puck = dom.puck;
    puck.style.marginLeft = puck.offsetWidth / -2 + "px";
    puck.style.marginTop = puck.offsetHeight / -2 + "px";

    The main work of the program is done by the setInterval function. Note that this is a function call, not an assignment:

    setInterval(function() {
         // Function implementation here
         // ...
         }, 100);

    The setInterval method is one of the two JavaScript timer-based methods (the other is setTimeout; the difference between the two is that setInterval will not be queued by the runtime if a previous instance is already queued). Note that although setInterval returns a cancellation value, it is not used here. In this implementation, the only way to cancel readings from the accelerometer is to exit the widget.

    Here, setInterval() is passed two values:

    • an anonymous (lambda) function which is defined in the method call (JavaScript is a functional language remember, so functions are first class objects and can be passed around like this; and it's a dynamic language, so functions can be defined and invoked on the fly like this)
    • the value 100, which sets the timer interval to 100 milliseconds.

    What does the lambda function do? Firstly, it calculates an interval value which will be displayed, based on the current time and the lastUpdate time, and then it resets the value of lastUpdate:

    function() {
         /*
    	Calculate the actual interval duration.
         */
         var now = new Date();
         var interval = now - lastUpdate;
         lastUpdate = now;
         // ...

    Next, it invokes getAccelerometer to perform the JIL API query, and assigns the result object to info:

         // Query the accelerometer
         var info = app.getAccelerometer();
         // ...

    Next, it uses the info property values to set the textContent attributes of the info-table elements:

         // Update the accelerometer information table
         var dom = app.dom;
         dom.indicatorX.textContent = info.x.toFixed(4);
         // ... And so on for y, z
         dom.indicatorInterval.textContent = interval + "ms";
         // ...

    Finally, it updates the puck position by invoking placePuck, passing the info object:

         // Place the puck
         app.placePuck(info);
         } // End of function definition

    The first call to setInterval is made when the init method is loaded, and thereafter the runtime will call setInterval each time the timer counts down 100 milliseconds. The difference between 100 and the actual displayed interval is an approximation of the latency in making the call. Note that the latency is not just the time taken to execute the calling code, because it depends on the total current load on the runtime and also implicitly on the load on the device itself. (Also, it's only an approximation, because the 100 millisecond timer is itself subject to possible latencies.)

    Common

    The widget samples share a common folder which includes css, img, and js folders.

    The common files define basic typographic and layout CSS properties, some JavaScript utility functions (the Accelerometer widget doesn't use any), and include the Vodafone logo as a PNG (displayed in the standard footer the sample widgets all share).

    • base.css is noteworthy because it defines font and element sizes, and uses media queries.
    • layout.css is noteworthy because it defines basic attributes of the widget layout including the widget height and width, absolute/relative positioning for different page elements, and overflow properties.

    Concluding remarks

    All in all, this is a simple but elegant demonstration of effective JavaScript programming using JIL APIs:

    • Note the repeated use of scope chain reduction to minimise access times for JavaScript DOM methods.
    • Note the simplicity of using the JIL API itself here, i.e. all that is required are simple accesses to object properties

    Find out more

    Two great books that every JavaScript developer should read are:

    • JavaScript: The Good Parts (Crockford, 2008, O'Reilly) – a short and lucid introduction to the best of JavaScript, with many insights on how to get the best out of the language.
    • High Performance JavaScript (Zakas, 2010, O'Reilly) – includes a thorough treatment of what the scope chain is and why optimising it matters.

    Also relevant is:

    • High Performance Web Sites (Souders, 2007, O'Reilly) – covers essential HTML optimisation techniques, many of which are relevant on mobile too.