Geolocation and Accelerometer APIs

Exclusive offer: get 50% off this eBook here
Flash iOS Apps Cookbook

Flash iOS Apps Cookbook — Save 50%

100 practical recipes for developing iOS apps with Flash Professional and Adobe AIR with this book and ebook

$29.99    $15.00
by Christopher Caleb | January 2012 | Cookbooks

Devices such as the iPhone are also location-aware; its GPS receiver is able to determine its position anywhere in the world. Movement can be tracked, the current speed can be obtained, and even the direction the device is facing can be determined. In addition to mapping, location services are finding their way into all kinds of areas ranging from photography to messaging clients.

In this article written by Christopher Caleb, author of Flash iOS Apps Cookbook, we will cover the following recipes:

  • Determining your current location
  • Determining your speed and heading
  • Checking for geolocation access
  • Responding to accelerometer changes
  • Detecting a shake

(For more resources on iOS, see here.)

The iOS family makes use of many onboard sensors including the three-axis accelerometer, digital compass, camera, microphone, and global positioning system (GPS). Their inclusion has created a world of opportunity for developers, and has resulted in a slew of innovative, creative, and fun apps that have contributed to the overwhelming success of the App Store.

Determining your current location

The iOS family of devices are location-aware, allowing your approximate geographic position to be determined. How this is achieved depends on the hardware present in the device. For example, the original iPhone, all models of the iPod touch, and Wi-Fi-only iPads use Wi-Fi network triangulation to provide location information. The remaining devices can more accurately calculate their position using an on-board GPS chip or cell-phone tower triangulation.

The AIR SDK provides a layer of abstraction that allows you to extract location information in a hardware-independent manner, meaning you can access the information on any iOS device using the same code.

This recipe will take you through the steps required to determine your current location.

Getting ready

An FLA has been provided as a starting point for this recipe.

From Flash Professional, open chapter9\recipe1\recipe.fla from the code bundle which can be downloaded from http://www.packtpub.com/support.

 

How to do it...

Perform the following steps to listen for and display geolocation data:

  1. Create a document class and name it Main.
  2. Import the following classes and add a member variable of type Geolocation:

    package {
    import flash.display.MovieClip;
    import flash.events.GeolocationEvent;
    import flash.sensors.Geolocation;
    public class Main extends MovieClip {
    private var geo:Geolocation;
    public function Main() {
    // constructor code
    }
    }
    }

  3. Within the class' constructor, instantiate a Geolocation object and listen for updates from it:

    public function Main() {
    if(Geolocation.isSupported)
    {
    geo = new Geolocation();
    geo.setRequestedUpdateInterval(1000);
    geo.addEventListener(GeolocationEvent.UPDATE, geoUpdated);
    }
    }

  4. Now, write an event handler that will obtain the updated geolocation data and populate the dynamic text fields with it:

    private function geoUpdated(e:GeolocationEvent):void {
    latitudeField.text = e.latitude.toString();
    longitudeField.text = e.longitude.toString();
    altitudeField.text = e.altitude.toString();
    hAccuracyField.text = e.horizontalAccuracy.toString();
    vAccuracyField.text = e.verticalAccuracy.toString();
    timestampField.text = e.timestamp.toString();
    }

  5. Save the class file as Main.as within the same folder as the FLA. Move back to the FLA and save it too.
  6. Publish and test the app on your device.
  7. When launched for the first time, a native iOS dialog will appear.
  8. Tap the OK button to grant your app access to the device's location data.

Devices running iOS 4 and above will remember your choice, while devices running older versions of iOS will prompt you each time the app is launched.

The location data will be shown on screen and periodically updated. Take your device on the move and you will see changes in the data as your geographical location changes.

How it works...

AIR provides the Geolocation class in the flash.sensors package, allowing the location data to be retrieved from your device. To access the data, create a Geolocation instance and listen for it dispatching GeolocationEvent.UPDATE events.

We did this within our document class' constructor, using the geo member variable to hold a reference to the object:

geo = new Geolocation();
geo.setRequestedUpdateInterval(1000);
geo.addEventListener(GeolocationEvent.UPDATE, geoUpdated);

The frequency with which location data is retrieved can be set by calling the Geolocation. setRequestedUpdateInterval() method. You can see this in the earlier code where we requested an update interval of 1000 milliseconds. This only acts as a hint to the device, meaning the actual time between updates may be greater or smaller than your request. Omitting this call will result in the device using a default update interval. The default interval can be anything ranging from milliseconds to seconds depending on the device's hardware capabilities.

Each UPDATE event dispatches a GeolocationEvent object, which contains properties describing your current location. Our geoUpdated() method handles this event by outputting several of the properties to the dynamic text fields sitting on the stage:

private function geoUpdated(e:GeolocationEvent):void {
latitudeField.text = e.latitude.toString();
longitudeField.text = e.longitude.toString();
altitudeField.text = e.altitude.toString();
hAccuracyField.text = e.horizontalAccuracy.toString();
vAccuracyField.text = e.verticalAccuracy.toString();
timestampField.text = e.timestamp.toString();
}

The following information was output:

  • Latitude and longitude
  • Altitude
  • Horizontal and vertical accuracy
  • Timestamp

The latitude and longitude positions are used to identify your geographical location. Your altitude is also obtained and is measured in meters. As you move with the device, these values will update to reflect your new location.

The accuracy of the location data is also shown and depends on the hardware capabilities of the device. Both the horizontal and vertical accuracy are measured in meters.

Finally, a timestamp is associated with every GeolocationEvent object that is dispatched, allowing you to determine the actual time interval between each. The timestamp specifies the milliseconds that have passed since the app was launched.

Some older devices that do not include a GPS unit only dispatch UPDATE events occasionally. Initially, one or two UPDATE events are dispatched, with additional events only being dispatched when location information changes noticeably.

Also note the use of the static Geolocation.isSupported property within the constructor. Although this will currently return true for all iOS devices, it cannot be guaranteed for future devices. Checking for geolocation support is also advisable when writing cross-platform code.

For more information, perform a search for flash.sensors.Geolocation and flash. events.GeolocationEvent within Adobe Community Help.

There's more...

The amount of information made available and the accuracy of that information depends on the capabilities of the device.

Accuracy

The accuracy of the location data depends on the method employed by the device to calculate your position. Typically, iOS devices with an on-board GPS chip will have a benefit over those that rely on Wi-Fi triangulation.

For example, running this recipe's app on an iPhone 4, which contains a GPS unit, results in a horizontal accuracy of around 10 meters. The same app running on a third-generation iPod touch and relying on a Wi-Fi network, reports a horizontal accuracy of around 100 meters. Quite a difference!

Altitude support

The current altitude can only be obtained from GPS-enabled devices. On devices without a GPS unit, the GeolocationEvent.verticalAccuracy property will return -1 and GeolocationEvent.altitude will return 0. A vertical accuracy of -1 indicates that altitude cannot be detected.

You should be aware of, and code for these restrictions when developing apps that provide location-based services. Do not make assumptions about a device's capabilities.

If your application relies on the presence of GPS hardware, then it is possible to state this within your application descriptor file. Doing so will prevent users without the necessary hardware from downloading your app from the App Store.

Mapping your location

The most obvious use for the retrieval of geolocation data is mapping. Typically, an app will obtain a geographic location and display a map of its surrounding area. There are several ways to achieve this, but launching and passing location data to the device's native maps application is possibly the easiest solution.

If you would prefer an ActionScript solution, then there is the UMap ActionScript 3.0 API, which integrates with map data from a wide range of providers including Bing, Google, and Yahoo!. You can sign up and download the API from www.umapper.com. Also tutorials are available at www.afcomponents.com/tutorials/umap_as3.

Calculating distance between geolocations

When the geographic coordinates of two separate locations are known, it is possible to determine the distance between them. AIR does not provide an API for this but an AS3 solution can be found on the Adobe Developer Connection website at: http://cookbooks.adobe.com/index.cfm?event=showdetails&postId=5701.

The UMap ActionScript 3.0 API can also be used to calculate distances. Refer to www.umapper.com.

Geocoding

Mapping providers, such as Google and Yahoo!, provide geocoding and reverse-geocoding web services. Geocoding is the process of finding the latitude and longitude of an address, whereas reverse-geocoding converts a latitude-longitude pair into a readable address.

You can make HTTP requests from your AIR for iOS application to any of these services. As an example, take a look at the Yahoo! PlaceFinder web service at http://developer.yahoo.com/geo/placefinder.

Alternatively, the UMap ActionScript 3.0 API integrates with many of these services to provide geocoding functionality directly within your Flash projects. Refer to the uMapper website.

Gyroscope support

Another popular sensor is the gyroscope, which is found in more recent iOS devices. While the AIR SDK does not directly support gyroscope access, Adobe has made available a native extension for AIR 3.0, which provides a Gyroscope ActionScript class.

A download link and usage examples can be found on the Adobe Developer Connection site at www.adobe.com/devnet/air/native-extensions-for-air/extensions/gyroscope.html.

Determining your speed and heading

The availability of an on-board GPS unit makes it possible to determine your speed and heading. In this recipe, we will write a simple app that uses the Geolocation class to obtain and use this information. In addition, we will add compass functionality by utilizing the user's current heading.

Getting ready

You will need a GPS-enabled iOS device. The iPhone has featured an on-board GPS unit since the release of the 3G. GPS hardware can also be found in all cellular network-enabled iPads.

From Flash Professional, open chapter9\recipe2\recipe.fla from the code bundle.

Sitting on the stage are three dynamic text fields. The first two (speed1Field and speed2Field) will be used to display the current speed in meters per second and miles per hour respectively. We will write the device's current heading into the third—headingField.

Also, a movie clip named compass has been positioned near the bottom of the stage and represents a compass with north, south, east, and west clearly marked on it. We will update the rotation of this clip in response to heading changes to ensure that it always points towards true north.

How to do it...

To obtain the device's speed and heading, carry out the following steps:

  1. Create a document class and name it Main.
  2. Add the necessary import statements, a constant, and a member variable of type Geolocation:

    package {
    import flash.display.MovieClip;
    import flash.events.GeolocationEvent;
    import flash.sensors.Geolocation;
    public class Main extends MovieClip {
    private const CONVERSION_FACTOR:Number = 2.237;
    private var geo:Geolocation;
    public function Main() {
    // constructor code
    }
    }
    }

  3. Within the constructor, instantiate a Geolocation object and listen for updates:

    public function Main() {
    if(Geolocation.isSupported)
    {
    geo = new Geolocation();
    geo.setRequestedUpdateInterval(50);
    geo.addEventListener(GeolocationEvent.UPDATE, geoUpdated);
    }
    }

  4. We will need an event listener for the Geolocation object's UPDATE event. This is where we will obtain and display the current speed and heading, and also update the compass movie clip to ensure it points towards true north. Add the following method:

    private function geoUpdated(e:GeolocationEvent):void {
    var metersPerSecond:Number = e.speed;
    var milesPerHour:uint = getMilesPerHour(metersPerSecond);
    speed1Field.text = String(metersPerSecond);
    speed2Field.text = String(milesPerHour);
    var heading:Number = e.heading;
    compass.rotation = 360 - heading;
    headingField.text = String(heading);
    }

  5. Finally, add this support method to convert meters per second to miles per hour:

    private function getMilesPerHour(metersPerSecond:Number):uint
    {
    return metersPerSecond * CONVERSION_FACTOR;
    }

  6. Save the class file as Main.as. Move back to the FLA and save it too.
  7. Compile the FLA and deploy the IPA to your device.
  8. Launch the app. When prompted, grant your app access to the GPS unit.

Hold the device in front of you and start turning on the spot. The heading (degrees) field will update to show the direction you are facing. The compass movie clip will also update, showing you where true north is in relation to your current heading.

Take your device outside and start walking, or better still, start running. On average every 50 milliseconds you will see the top two text fields update and show your current speed, measured in both meters per second and miles per hour.

How it works...

In this recipe, we created a Geolocation object and listened for it dispatching UPDATE events. An update interval of 50 milliseconds was specified in an attempt to receive the speed and heading information frequently.

Both the speed and heading information are obtained from the GeolocationEvent object, which is dispatched on each UPDATE event. The event is captured and handled by our geoUpdated() handler, which displays the speed and heading information from the accelerometer.

The current speed is measured in meters per second and is obtained by querying the GeolocationEvent.speed property. Our handler also converts the speed to miles per hour before displaying each value within the appropriate text field. The following code does this:

var metersPerSecond:Number = e.speed;
var milesPerHour:uint = getMilesPerHour(metersPerSecond);
speed1Field.text = String(metersPerSecond);
speed2Field.text = String(milesPerHour);

The heading, which represents the direction of movement (with respect to true north) in degrees, is retrieved from the GeolocationEvent.heading property. The value is used to set the rotation property of the compass movie clip and is also written to the headingField text field:

var heading:Number = e.heading;
compass.rotation = 360 - heading;
headingField.text = String(heading);

The remaining method is getMilesPerHour() and is used within geoUpdated() to convert the current speed from meters per second into miles per hour. Notice the use of the CONVERSION_FACTOR constant that was declared within your document class:

private function getMilesPerHour(metersPerSecond:Number):uint
{
return metersPerSecond * CONVERSION_FACTOR;
}

Although the speed and heading obtained from the GPS unit will suffice for most applications, the accuracy can vary across devices. Your surroundings can also have an affect; moving through streets with tall buildings or under tree coverage can impair the readings.

You can find more information regarding flash.sensors.Geolocation and flash.events.GeolocationEvent within Adobe Community Help.

There's more...

The following information provides some additional detail.

Determining support

Your current speed and heading can only be determined by devices that possess a GPS receiver.

Although you can install this recipe's app on any iOS device, you won't receive valid readings from any model of iPod touch, the original iPhone, or W-Fi-only iPads. Instead the GeolocationEvent.speed property will return -1 and GeolocationEvent.heading will return NaN.

If your application relies on the presence of GPS hardware, then it is possible to state this within the application descriptor file. Doing so will prevent users without the necessary hardware from downloading your app from the App Store.

Simulating the GPS receiver

During the development lifecycle it is not feasible to continually test your app in a live environment. Instead you will probably want to record live data from your device and re-use it during testing. There are various apps available that will log data from the sensors on your device.

One such app is xSensor, which can be downloaded from iTunes or the App Store and is free. Its data sensor log is limited to 5KB but this restriction can be lifted by purchasing xSensor Pro.

Preventing screen idle

Many of this article's apps don't require you to touch the screen that often. Therefore you will be likely to experience the backlight dimming or the screen locking while testing them. This can be inconvenient and can be prevented by disabling screen locking.

 

 

Flash iOS Apps Cookbook 100 practical recipes for developing iOS apps with Flash Professional and Adobe AIR with this book and ebook
Published: February 2012
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

(For more resources on iOS, see here.)

Checking for geolocation access

Applications that make use of a device's location data must be granted permission by the user. The user is prompted when an app attempts to access location data for the first time. Devices running iOS 4 and above will remember this choice, whereas older versions of iOS will request access each time the app is launched. In addition, access privileges can be changed at any time from the device's settings.

It is important that your app can detect the availability of geolocation data, and also respond to permission changes at runtime. Let us see how this is done.

Getting ready

An FLA has been provided as a starting point.

From the code bundle, open chapter9\recipe3\recipe.fla into Flash Professional.

A dynamic text field with an instance name of output has been added to the stage.

We will write an app that listens for the availability of the geolocation data and reports any changes to the output text field.

How to do it...

Carry out the following steps:

  1. Create a document class and name it Main.
  2. Add the following import statements and a member variable of type Geolocation:

    package {
    import flash.display.MovieClip;
    import flash.events.GeolocationEvent;
    import flash.events.StatusEvent;
    import flash.sensors.Geolocation;
    public class Main extends MovieClip {
    private var geo:Geolocation;
    public function Main() {
    // constructor code
    }
    }
    }

  3. Within the constructor, create a Geolocation object and listen for it dispatching GeolocationEvent.UPDATE and StatusEvent.STATUS:

    public function Main() {
    output.text = "Obtaining location...";
    if(Geolocation.isSupported)
    {
    geo = new Geolocation();
    geo.setRequestedUpdateInterval(1000);
    geo.addEventListener(GeolocationEvent.UPDATE, geoUpdated);
    geo.addEventListener(StatusEvent.STATUS, statusUpdated);
    }
    else
    {
    output.text = "Geolocation is not supported.";
    }
    }

  4. Finally, add a handler for each event:

    private function geoUpdated(e:GeolocationEvent):void {
    output.text = "Location received.";
    }
    private function statusUpdated(e:StatusEvent):void {
    if(e.code == "Geolocation.Muted")
    {
    output.text = "Geolocation access denied.";
    }
    }

  5. Save the class file as Main.as.
  6. Save the FLA and publish it. Install the IPA and launch it on your device. A native iOS dialog will appear.
  7. Tap the Don't Allow button to deny the app access to the device's location data. The following text will appear on-screen: Obtaining location... and will quickly be replaced with: Geolocation access denied.
  8. Now re-launch the app. If you are using iOS 4 or above, your previous setting will be remembered and access to the geolocation data will once again be denied. If you are using an earlier version of iOS, then you will be prompted to grant access each time. On devices running iOS 4 and above, an app's access privileges can be changed from the device's settings. Let us do this for our app.
  9. To re-launch an app in iOS 4 or above, you will first need to kill it using the fast app switcher.

  10. Exit from the app by pressing the Home button. From the device's settings, move to Location Services. You will be presented with a list of apps that have attempted to access location data. Scroll down until you find c9 r3. Tap the button next to it to allow access to it.
  11. Now move back to the home screen and launch this recipe's app again. This time you will see the following text: Obtaining location... and it will quickly be replaced with: Location received.

How it works...

When the user prevents an AIR for iOS app from accessing location data, GeolocationEvent.UPDATE events cease and StatusEvent.STATUS is dispatched from the Geolocation object. It is therefore possible to determine when access to location data has been revoked by simply listening for the STATUS event:

geo = new Geolocation();
geo.setRequestedUpdateInterval(1000);
geo.addEventListener(GeolocationEvent.UPDATE, geoUpdated);
geo.addEventListener(StatusEvent.STATUS, statusUpdated);

A final check is performed within the statusUpdated() handler:

private function statusUpdated(e:StatusEvent):void {
if(e.code == "Geolocation.Muted")
{
output.text = "Geolocation access denied.";
}
}

In this method, we query the StatusEvent object's code property. If it has a string value of Geolocation.Muted, then we know that access to the geolocation data is no longer available.

Finally, although it wasn't used in this recipe's example, you can also access the Geolocation.muted property to determine if geolocation data is available. When a newly installed app is launched for the first time, muted will be set to true until the user grants permission from the native iOS dialog.

More information regarding flash.events.StatusEvent can be found in Adobe Community Help.

Responding to accelerometer changes

The accelerometer provides access to data that represents the device's location or movement along a three-dimensional axis. When motion is detected, it is returned as data, which can be accessed by ActionScript.

This recipe will show you how to take advantage of the accelerometer found in iOS devices.

Getting ready

An FLA has been provided as a starting point.

Open chapter9\recipe4\recipe.fla from the code bundle into Flash Professional.

You will find five dynamic text fields positioned on the stage. Below them is a movie clip with an instance name of arrow. We will populate each text field with data retrieved from the device's accelerometer and rotate the movie clip to reflect physical changes in the device's orientation.

Also notice the stage's dimensions are set to 480x320. For this recipe, landscape orientation will be used.

How to do it...

Perform these steps to listen for and respond to accelerometer changes:

  1. Create a document class and name it Main.
  2. Add the following two import statements and a member variable of type Accelerometer:

    package {
    import flash.display.MovieClip;
    import flash.events.AccelerometerEvent;
    import flash.sensors.Accelerometer;
    public class Main extends MovieClip {
    private var acc:Accelerometer;
    public function Main() {
    // constructor code
    }
    }
    }

  3. Within the constructor, instantiate an Accelerometer object and listen for updates from it:

    public function Main() {
    if(Accelerometer.isSupported)
    {
    acc = new Accelerometer();
    acc.setRequestedUpdateInterval(50);
    acc.addEventListener(AccelerometerEvent.UPDATE,
    accUpdated);
    }
    }

  4. Finish by writing an event handler that will obtain and use the updated accelerometer data:

    private function accUpdated(e:AccelerometerEvent):void {
    var radians:Number = Math.atan2(e.accelerationY,
    e.accelerationX);
    var degrees:Number = (radians * (180 / Math.PI)) - 90;
    arrow.rotation = -degrees;
    accXField.text = e.accelerationX.toString();
    accYField.text = e.accelerationY.toString();
    accZField.text = e.accelerationZ.toString();
    timeField.text = e.timestamp.toString();
    rotField.text = degrees.toString();
    }

  5. Save the class file as Main.as.
  6. Also, save your FLA and then publish it. Install the app to your device and launch it.

As the device's motion sensor detects activity, the text fields will update. Holding the device in front of yourself and tilting it clockwise and counter-clockwise will update the rotation of the arrow movie clip, ensuring that it always points upwards.

How it works...

We accessed the accelerometer's data by creating an instance of the Accelerometer class and listening for it dispatching the AccelerometerEvent.UPDATE event:

acc = new Accelerometer();
acc.setRequestedUpdateInterval(50);
acc.addEventListener(AccelerometerEvent.UPDATE, accUpdated);

The frequency with which UPDATE events are received can be set by calling the Accelerometer.setRequestedUpdateInterval() method. You can see this in the earlier code where we requested an update every 50 milliseconds. This only acts as a hint to the device, meaning the actual time between updates may be greater or smaller than your request. Omitting this call will result in the device using a default update interval. The default interval can be anything ranging from milliseconds to seconds depending on the device's hardware capabilities.

The UPDATE event is an AccelerometerEvent object and provides access to the following properties:

  • accelerationX—Acceleration along the x-axis. When the device is upright, the x-axis runs from left to right. Acceleration is positive if the device is moved to the right.
  • accelerationY—Acceleration along the y-axis. When the device is upright, the y-axis runs from bottom to top. Acceleration is positive if the device is moved upwards.
  • accelerationZ—Acceleration along the z-axis. The acceleration is positive if the device is moved so that its face points upwards. Acceleration is negative if it faces towards the ground.
  • timestamp—The number of milliseconds that have elapsed since the app was launched.

Acceleration is measured in "g" with 1g being the standard acceleration due to gravity, which is approximately 9.8 meters per second squared.

We obtain these properties within the accUpdated() handler and write them to our dynamic text fields:

accXField.text = e.accelerationX.toString();
accYField.text = e.accelerationY.toString();
accZField.text = e.accelerationZ.toString();
timeField.text = e.timestamp.toString();

Additionally, the accelerometerX and accelerometerY properties are used to calculate the angle at which the device is being held (with the screen facing you). This is used to update the rotation of the arrow movie clip. The following is the code that does this:

var radians:Number = Math.atan2(e.accelerationY,
e.accelerationX);
var degrees:Number = (radians * (180 / Math.PI)) - 90;
arrow.rotation = -degrees;

Knowing the angle, at which the device is being tilted, is useful for many applications. In particular games, where tilting the device may be used to move a character along a platform or simulate the movement of a steering wheel.

Finally, within the constructor, note the use of the static read-only property Accelerometer.isSupported to check for the availability of an accelerometer. The accelerometer is supported on all existing iOS devices but isn't guaranteed for future devices. It is therefore a good practice to check for support and is also beneficial when writing cross-platform code.

There's more...

The following is some more information regarding the accelerometer and how to work with its data.

Orientation and the accelerometer axes

The accelerometer axes are re-oriented with the device's display rather than the physical orientation of the device itself. In other words, when auto-orientation is active, the y-axis will be vertical when the display's content is being viewed in a normal up-right position. This is true for both apps that default to a portrait aspect-ratio and apps that default to a landscape aspect-ratio. If however, auto-orientation is not active, then the accelerometer axes will not be re-oriented when the device is rotated.

Determining device orientation

Data from the accelerometer is affected by gravity and can be useful to determine the device's current orientation. The following are the values to check for:

  • accelerationX > 0.5—Rotated 90 degrees counter clockwise
  • accelerationX < -0.5—Rotated 90 degrees clockwise
  • accelerationY > 0.5—Normal upright position
  • accelerationY < -0.5—Upside down
  • accelerationZ > 0.5—Face up
  • accelerationZ < -0.5—Face down

This provides an alternative to determining orientation by listening for StageOrientationEvent objects being dispatched from the stage. In addition, using the acceleration data makes it possible to determine whether the device's screen display is facing upwards or towards the ground.

Applying a low-pass filter

Data from the accelerometer is affected by both the effect of gravity and sudden changes in motion. If you are using this data to detect the device's orientation, then you should isolate the gravity component from the data by applying a low-pass filter.

This can be achieved by smoothing out the data over time. To do this, start by creating a filtering factor and three member variables to store the previous value for each axis:

private const FACTOR:Number = 0.1;
private var accX:Number = 0;
private var accY:Number = 0;
private var accZ:Number = 0;

Now in response to each AccelerometerEvent.UPDATE, apply a low-pass filter to keep only the gravity component from each axis:

accX = (e.accelerationX * FACTOR) + (accX * (1 - FACTOR));
accY = (e.accelerationY * FACTOR) + (accY * (1 - FACTOR));
accZ = (e.accelerationZ * FACTOR) + (accZ * (1 - FACTOR));

Essentially this code generates a value for each axis that uses 10 percent of its current data and 90 percent of the previously filtered data. This will ensure that data responds slowly to sudden and short-lived changes in motion.

Applying a high-pass filter

Many types of applications use accelerometer data to detect sudden changes in motion. A high-pass filter can be used to isolate the portion of the data that is caused by sudden changes in motion.

Similar to the implementation of a low-pass filter, use a filtering factor plus three member variables to store the previous value for each axis. Then in response to each AccelerometerEvent.UPDATE, apply the filter:

accX = e.accelerationX - ((e.accelerationX * FACTOR) +
(accX * (1 - FACTOR)));
accY = e.accelerationY - ((e.accelerationY * FACTOR) +
(accY * (1 - FACTOR)));
accZ = e.accelerationZ - ((e.accelerationZ * FACTOR) +
(accZ * (1 - FACTOR)));

In this example, a low-pass filter value is calculated for each axis and subtracted from the current value. Doing so keeps the sudden changes in motion while removing the gravity component.

The "muted" property

The Accelerometer class has a static read-only property named muted. It is used to determine if a user has granted the app permission to access accelerometer data. This property isn't required for iOS as there is no way, at present, to deny an app access to the accelerometer.

Flash iOS Apps Cookbook 100 practical recipes for developing iOS apps with Flash Professional and Adobe AIR with this book and ebook
Published: February 2012
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

(For more resources on iOS, see here.)

Detecting a shake

A common use of the accelerometer is to detect a shake and this has become a popular method of interaction in games and applications. For example, many of the apps that come with iOS allow the user to perform an undo by shaking the device.

This recipe will show you how to determine if the user is shaking their device by examining the data coming from the accelerometer.

Getting ready

From the code bundle, open chapter9\recipe5\recipe.fla into Flash Professional.

You will find a movie clip named shake sitting in the center of the stage. Its timeline consists of two key-frames.

We will write some ActionScript that will move the clip in response to changes along the device's three axes. When the motion is pronounced, we will indicate to the user that a shake has been detected by jumping to the movie clip's second frame.

The stage uses a landscape aspect ratio for this recipe.

How to do it...

Perform the following steps to detect a shake:

  1. Create a document class and name it Main.
  2. Import the classes required to work with the accelerometer and add the following member variables:

    package {
    import flash.display.MovieClip;
    import flash.events.AccelerometerEvent;
    import flash.sensors.Accelerometer;
    public class Main extends MovieClip {
    private const THRESHOLD:Number = 0.8;
    private var acc:Accelerometer;
    private var prevX:Number;
    private var prevY:Number;
    private var prevZ:Number;
    private var startX:Number;
    private var startY:Number;
    public function Main() {
    // constructor code
    }
    }
    }

  3. Initialize the member variables and the shake movie clip. Also, instantiate an Accelerometer object and listen for it dispatching UPDATE events:

    public function Main() {
    prevX = prevY = prevZ = 0;
    shake.gotoAndStop(1);
    startX = shake.x;
    startY = shake.y;
    if(Accelerometer.isSupported)
    {
    acc = new Accelerometer();
    acc.setRequestedUpdateInterval(50);
    acc.addEventListener(AccelerometerEvent.UPDATE,
    accUpdated);
    }
    }

  4. Add a handler for the UPDATE event:

    private function accUpdated(e:AccelerometerEvent):void {
    var changeX:Number = prevX - e.accelerationX;
    var changeY:Number = prevY - e.accelerationY;
    var changeZ:Number = prevZ - e.accelerationZ;
    prevX = e.accelerationX;
    prevY = e.accelerationY;
    prevZ = e.accelerationZ;
    shake.x = startX + (changeX * 100);
    shake.y = startY + (changeY * 100);
    shake.z = (changeZ * 100);
    if(Math.abs(changeX) > THRESHOLD ||
    Math.abs(changeY) > THRESHOLD ||
    Math.abs(changeZ) > THRESHOLD)
    {
    shake.gotoAndStop(2);
    }
    else
    {
    shake.gotoAndStop(1);
    }
    }

  5. Save the class file and when prompted name it Main.as.
  6. Now save the FLA and publish it. Test the app on your device.

Gently shaking the device will displace the movie clip from the center of the screen. A more violent motion will also change its appearance indicating that a sufficiently large shake has been detected.

How it works...

This example compares the current acceleration data with the previous to see if a sufficiently large change has occurred.

The previous acceleration data is stored within the prevX, prevY, and prevZ member variables and is compared against the current data to etermine the change for each axis:

var changeX:Number = prevX - e.accelerationX;
var changeY:Number = prevY - e.accelerationY;
var changeZ:Number = prevZ - e.accelerationZ;
prevX = e.accelerationX;
prevY = e.accelerationY;
prevZ = e.accelerationZ;

If the change in any one of the three axes is large enough, then it is safe to assume that the device is being shaken. This information is fed back to the user by moving to the second frame of the shake movie clip. The following is the code that does this:

if(Math.abs(changeX) > THRESHOLD ||
Math.abs(changeY) > THRESHOLD ||
Math.abs(changeZ) > THRESHOLD)
{
shake.gotoAndStop(2);
}
else
{
shake.gotoAndStop(1);
}

The THRESHOLD constant simply dictates the amount of change that needs to take place in any of the axes for the motion to be deemed a shake. Lowering this value will reduce the effort required by the user to trigger a shake, while increasing it will make it more difficult.

To provide greater visual feedback, we also update the x, y, and z positions of the shake movie clip in response to changes from the accelerometer. The more violent the shaking motion, the more the clip is displaced from its original starting position:

shake.x = startX + (changeX * 100);
shake.y = startY + (changeY * 100);
shake.z = (changeZ * 100);

The startX and startY member variables used are initialized within the constructor and are set to the shake movie clip's original position.

The majority of the work in this example is performed within the accUpdated() event handler, which is called each time AccelerometerEvent.UPDATE is dispatched from the Accelerometer object. To ensure the app is responsive, a call is made to the setRequestedUpdateInterval() method requesting frequent updates.

There's more...

Let us look at some options to further improve this recipe's example.

Checking multiple axes

We checked for a large enough change in only one of the axes before deciding that the user was shaking the device. Another approach is to wait for a significant change in two of the three axes. The code for this would look as follows:

var changeX:Number = Math.abs(e.accelerationX);
var changeY:Number = Math.abs(e.accelerationY);
var changeZ:Number = Math.abs(e.accelerationZ);
if((changeX > THRESHOLD && changeY > THRESHOLD) ||
(changeX > THRESHOLD && changeZ > THRESHOLD) ||
(changeY > THRESHOLD && changeZ > THRESHOLD))
{
// Shake detected.
}

For multiple axes you may want to reduce the THRESHOLD value slightly to compensate for the fact that the user must make a more exaggerated motion to initiate a shake.

Smoothing accelerometer data

You may have noticed that even when holding the device still, the shake movie clip shudders slightly. Accelerometers aren't perfectly accurate and the data returned will contain some noise.

This noise can be reduced by applying a high-pass filter to your data over time and is detailed in the Responding to accelerometer changes recipe. Try experimenting with the filtering factor and the threshold constant until you find values that give you a result you are happy with.

Summary

This article covered recipes that utilized both the device's GPS sensor and its accelerometer. We learnt how to make location-aware apps, respond to changes in physical orientation, and detect vibration.


Further resources on this subject:


About the Author :


Christopher Caleb

Christopher Caleb is lead Flash developer at WeeWorld - an avatar-based social game and virtual world for teens. He's particularly passionate about mobile development and has considerable experience creating Flash-based apps and prototypes for various platforms including Symbian, iOS and Android. You can find Christopher's random thoughts and links to articles he's written at: http://www.yeahbutisitflash.com. You can also follow him on Twitter: @chriscaleb

Books From Packt


iOS 5 Essentials
iOS 5 Essentials

Unity iOS Essentials
Unity iOS Essentials

Unity iOS Game Development Beginners Guide
Unity iOS Game Development Beginners Guide

iOS Development using MonoTouch Cookbook
iOS Development using MonoTouch Cookbook

iPhone Applications Tune-Up
iPhone Applications Tune-Up

Flash Game Development by Example
Flash Game Development by Example

Flash Facebook Cookbook
Flash Facebook Cookbook

Flash Development for Android Cookbook
Flash Development for Android Cookbook


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
i
Q
j
p
F
i
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software