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.

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:

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:

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.

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.

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.

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:

Create a document class and name it Main.

Add the necessary import statements, a constant, and a member variable of type Geolocation:

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:

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:

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:

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:

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.

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:

Create a document class and name it Main.

Add the following import statements and a member variable of type Geolocation:

Save the FLA and publish it. Install the IPA and launch it on your device. A native iOS dialog will appear.

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.

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.

To re-launch an app in iOS 4 or above, you will first need to kill it using the fast app switcher.

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.

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:

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:

Create a document class and name it Main.

Add the following two import statements and a member variable of type Accelerometer:

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:

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:

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:

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:

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:

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.

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:

Create a document class and name it Main.

Import the classes required to work with the accelerometer and add the following member variables:

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:

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:

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:

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:

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.

Alerts & Offers

Series & Level

We understand your time is important. Uniquely amongst the major publishers, we seek to develop and publish the broadest range of learning and information products on each technology. Every Packt product delivers a specific learning pathway, broadly defined by the Series type. This structured approach enables you to select the pathway which best suits your knowledge level, learning style and task objectives.

Learning

As a new user, these step-by-step tutorial guides will give you all the practical skills necessary to become competent and efficient.

Beginner's Guide

Friendly, informal tutorials that provide a practical introduction using examples, activities, and challenges.

Essentials

Fast paced, concentrated introductions showing the quickest way to put the tool to work in the real world.

Cookbook

A collection of practical self-contained recipes that all users of the technology will find useful for building more powerful and reliable systems.

Blueprints

Guides you through the most common types of project you'll encounter, giving you end-to-end guidance on how to build your specific solution quickly and reliably.

Mastering

Take your skills to the next level with advanced tutorials that will give you confidence to master the tool's most powerful features.

Starting

Accessible to readers adopting the topic, these titles get you into the tool or technology so that you can become an effective user.

Progressing

Building on core skills you already have, these titles share solutions and expertise so you become a highly productive power user.