Home > Articles > Programming > C#

This chapter is from the book

Sensor Input

Devices such as touchscreens, mouse devices, styluses, and keyboards provide interactivity by allowing an app to respond to their interactions with components shown on their device displays. Users tap elements drawn to the screen or type characters that will appear inside onscreen text regions. However, a class of input devices known as sensors can give a running app information about the device’s relationship to its physical environment. Examples of the information sensors gather include details about which way the device is facing, its velocity in any particular direction, its position on the globe, and how much light is shining on it at a given moment. Devices might or might not include one or more of these kinds of sensors.

The Windows Runtime API includes support for working with several different kinds of sensors and relaying the information they gather. These APIs not only enable an app to ask for sensor measurements, but they also provide events that can be subscribed to and, in most cases, the capability to throttle how often these events can be raised. Some of the environmental information that can be obtained through these APIs includes information about a device’s physical location, its movement and orientation, and how bright of an environment it is in.

The Example App

The SensorsExample project highlights a few different ways sensors can be used from within an application. The app features an instance of the interactive Bing Maps control surrounded by boxes that show information from and allow interaction with each of the various sensors. The boxes along the left side also allow the app to coordinate the information it receives from the sensors with the display of the Bing Maps control. The Location section allows the map to be centered at the current geolocation coordinates and also offers support for working with geofencing (the upcoming sections explain geofencing). The Compass section enables the app to set the map’s orientation to approximate the current compass heading (although the support offered for setting a specified heading in the Bing Maps control is currently somewhat limited). The Inclinometer section allows the map to be panned in concert with the direction in which the device itself is being tilted.

Working with the Bing Maps Control

The Bing Maps control in the example project is part of the Bing Maps platform, which includes the Windows control, controls for other platforms, and several related data services. You can access information about the Bing Maps control and the related services and the tools and resources you need to include in your project through the Bing Maps Platform Portal.2 Although the control and the related services offer a tremendous amount of functionality, you need to be aware of some important license-related and technical considerations for this example application and in case you are considering their use in your own app.

From a licensing standpoint, some restrictions govern how this control can be used. The Bing Maps Platform Portal includes a Licensing Options page that explains how the restrictions apply to your app, under what circumstances the tools can be used for free, and when a fee needs to be paid to license the use of the control. As of this writing, you can access this page by clicking the Licensing link from the Bing Maps Platform Portal page. If you will use the Bing Maps control in your Windows Store App, be sure to look over the restrictions and conditions for use in the context of your application needs and ensure that you are abiding by the appropriate terms of use.

From a technical standpoint, before you can build the SensorsExample project, you need to download and install the Bing Maps SDK. You can get to the latest SDK installer by following links on the Bing Maps Platform Portal. Alternatively, you can use the Visual Studio Extension Manager to obtain the SDK.

To use the Extension Manager, launch Visual Studio and select Extensions and Updates from the Tools menu. In the Extensions and Updates dialog box, select the Online node and then select Visual Studio Gallery. Then type Bing Maps SDK into the search box in the upper-right corner (see Figure 13.1). In the search results, select the entry for Bing Maps SDK for Windows 8.1 Store Apps and click the Download button; then click the Install button in the ensuing dialog box after you have read and reviewed the included license agreement. After the installation has completed, you will most likely be instructed to restart Visual Studio so that you can use the installed SDK components.

FIGURE 13.1

FIGURE 13.1 Locating the Bing Maps SDK Visual Studio extension

Because this additional download and installation is required to build the example project, the build configuration in the WinRTByExample solution has been configured to not include the SensorsExample project as part of the solution build by default. To build the project, you need to either select the project in the Solution Explorer and choose Build from the project file’s context menu, or choose Build SensorsExample from the Build menu. Another option is to open the Configuration Manager entry from the Build menu and check the Build entry next to the SensorsExample project in the Configuration Manager dialog box that appears; the SensorsExample project then is built along with the other projects in the solution.

Another consideration when building a project that includes the Bing Maps control is the selection of a target platform. Most Windows Store apps are built with the target set to Any CPU. However, the Bing Maps control relies on the Visual C++ Runtime, which requires selecting a specific processor architecture to build a project that references it. You can set this value in the Configuration Manager dialog box. Select the appropriate Platform for your build either for the entire solution or for the SensorsExample project. For example, you need to select a value of ARM to create a version of the resulting app that will run on Windows RT devices. Note that in order to work with the XAML designer in Visual Studio, you need to select the value x86. If you prefer to have the interactive designer available, you can always set the value temporarily to x86 and then set it to your desired target platform when you have finished working in the XAML designer.

To deploy an app that includes the Bing Maps control, you need to specify a value for the map’s Credentials property. If you do not specify a valid map key for the map Credentials property, the map control displays with a banner indicating that invalid credentials are being used, as you can see in the example app screen in Figure 13.2.

FIGURE 13.2

FIGURE 13.2 The Bing Maps control displayed without valid credentials

The following markup shows the credentials being set in the example project:

<maps:Map Credentials="{StaticResource MapKey}"/>

In this example, the credentials are located in the resource defined by the value MapKey, which is defined in the project’s App.xaml file. The map key is a value you obtain from the Bing Maps Account Center.3 Sign into the account center with your Microsoft Account credentials and select Create or View Keys. At this point, you can define a new key by specifying information about your application or retrieve a previously defined key. Place this key value into the MapKey resource in your project, and build and run your project to make sure that the warning message from Figure 13.2 no longer displays.

Geolocation

Geolocation refers to information about an item’s geographic location. In the Windows Runtime, one of two data sources provides this location information. The first data source for location information is the Windows Location Provider. The Windows Location Provider obtains its information from a couple different data sources. The first source it attempts to use is Wi-Fi triangulation, in which the proximity to different known Wi-Fi hotspots is used to determine a position. If Wi-Fi data is not available, IP address resolution is then used. The second data source that the Windows Runtime can use to obtain location information is available if the device optionally includes one or more Global Positioning System (GPS) sensors.

The network-based information that the Windows Location Provider gathers is limited in both accuracy and amount of available detail because only latitude, longitude, and accuracy information are made available. An installed GPS sensor most likely provides more accurate information (different sensors have different resolution capabilities) and also gives more location information than the Windows Location Service, potentially including details about direction, speed, and altitude. Note, however, that the additional detail afforded by GPS sensors tends to come with additional power use and, therefore, reduced battery life.

Getting Started

To start working with location information in a Windows 8.1 application, you first need to declare that the app will be accessing this information. Location information is considered to be personally identifiable information (PII), so any app that will access this information needs to explicitly declare its intent to do so. The App Store’s certification process will refuse an app that includes use of the geolocation APIs if it does not provide such a declaration; if the app does provide the declaration, the app’s entry in the store will indicate its intent to access this information. As an additional measure meant to protect users, Windows notifies users the first time an app accesses location information and prompts them to either allow or block access. Windows also provides several places where the user can choose to toggle this same permission on or off, as will be discussed shortly. To declare that an app will attempt to access location information, open the app manifest file, select the Capabilities panel, and check the Location entry under the Capabilities list (see Figure 13.3).

FIGURE 13.3

FIGURE 13.3 Setting the location capability in the app manifest

Using the Geolocator

The Geolocator class provides location information in the Windows Runtime. You can obtain the current position value from this class in two ways. The first option is to directly request the current position with the GetGeopositionAsync method. The second option is to provide a handler for the PositionChanged event that is called when a position change is detected, depending on the configuration of the Geolocator instance.

The first time an app calls the GetGeopositionAsync method or registers an event handler for the PositionChanged event, the user is prompted to grant permission for the app to access location information, as Figure 13.4 illustrates. Because this step might display a user interface element, it is important to make sure that this first call takes place on the UI thread; otherwise, an unexpected cross-thread exception might occur whose cause can be difficult to diagnose.

FIGURE 13.4

FIGURE 13.4 Windows prompting the user for location information permission

The value selected in the prompt is reflected in the Permissions panel that you can access through the app’s Settings Charm, as well as within the Location panel in the Privacy section located in PC Settings. This system-wide location privacy screen lists all the applications that are registered to access position information and states whether access is currently blocked or enabled. It also includes a system-wide switch to disable access to location information for all apps that request it. However they access it, when users choose to block the app’s access to location information, the LocationStatus property on the Geolocator instance returns a value of PositionStatus.Disabled. The potential consequences of this LocationStatus value and other values that can appear in this property are discussed shortly.

In the example app, interactions with the Geolocator are handled in the GeolocationHelper class. The code in Listing 13.8 shows the Geolocator initialization and subscription to the available events.

LISTING 13.8 Geolocator Initialization and Event Subscription

_geolocator = new Geolocator();

// Listen for status change events, but also immediately get the status.
// This is in case it is already at its end-state and therefore
// won't generate a change event.
_geolocator.StatusChanged +=
    (o, e) => SetGeoLocatorReady(e.Status == PositionStatus.Ready);
SetGeoLocatorReady(_geolocator.LocationStatus == PositionStatus.Ready);

// Set the desired accuracy. Alternatively, can use
// DesiredAccuracyInMeters, where < 100 meters ==> high accuracy
_geolocator.DesiredAccuracy = GetDesiredPositionAccuracy();

// Listen for position changed events.
// Set to not report more often than once every 10 seconds
// and only when movement exceeds 50 meters
_geolocator.ReportInterval = 10000; // Value in ms
_geolocator.MovementThreshold = 50; // Value in meters
_geolocator.PositionChanged += GeolocatorOnPositionChanged;

The first task in the code in Listing 13.8 is to work with the LocationStatus value. The Disabled status was previously mentioned, but it is important to note that an attempt to request the current position from a Disabled instance results in an UnauthorizedAccessException. If location access has not been blocked, the LocationStatus property has a value of NoData either before the first call to GetGeopositionAsync or before the first time an event handler is provided for PositionChanged. When either of these happens, the Windows Runtime might trigger a startup sequence that takes a little time to complete. During that time, the LocationStatus returns a value of NotInitialized. Additionally, if location data is coming from a GPS sensor, the sensor tries to retrieve information from some required minimum number of satellites. Until the device reaches this number, the LocationStatus has a value of Initializing. When the Geolocator instance is ready, the LocationStatus returns a value of Ready. With all that in mind, when including the Geolocator in your project, be sure to account for the fact that, even under ideal circumstances, a lag might occur before it is ready to be used; you need to check to ensure that it has reached the Ready status.

In the example code, the SetGeoLocatorReady function is called with a value of true only when the sensor is in a Ready state. It is used to set the SensorSettings IsLocationAvailable property, which the application user interface uses to disable access to location retrieval functions. It also sets a local flag that prevents direct calls to get the current position through the GetCoordinate function from actually making the request through the Geolocator until it is in the Ready state.

The next step after working with initialization and status information involves establishing the desired accuracy for the Geolocator instance. The DesiredAccuracy property can be set to either PositionAccuracy.High or PositionAccuracy.Default. A value of High instructs WinRT to always try to use a GPS for its data if one is available, and to otherwise use the Windows Location Provider. A value of Default instructs WinRT to make use of only GPS sensors if it cannot obtain a value from the Windows Location Provider, such as when no Wi-Fi signals exist for triangulation (or the device is either not equipped or not configured to work with Wi-Fi) and when the device does not have an IP address that can be looked up for location information. Ultimately, setting either of these values does not guarantee how the WinRT will make use of GPS devices; it just indicates a preference for how it should behave.

The final task in Listing 13.8 is to configure how the Geolocator will go about raising PositionChanged events, which is controlled with the ReportInterval and MovementThreshold properties. Each of these properties limits how often the Geolocator instance can raise the PositionChanged events. Whereas the ReportInterval property specifies the minimum amount of time that must elapse between instances of the Windows Runtime attempting to obtain location information values, the MovementThreshold property indicates how much distance must pass before a subsequent event is raised. In the example code, the ReportInterval property is set to ensure that at least 10 seconds (10,000 milliseconds) pass between event updates. The MovementThreshold value is set to ensure that the position has changed by at least 50 meters. (The sensor is checked every 10 seconds, and the class instance raises an event only if the distance between checks exceeds 50 meters.) A value of 0 for ReportInterval generates events at whatever the maximum frequency is for the most accurate location source, and it should be used only for apps that require near-real-time position updates. Because it affects how often the location hardware is queried and, therefore, can impact battery life, it is important to set the ReportInterval to the maximum value possible for the needs of your app. Also note that not every scenario involving the Geolocator needs to subscribe to the PositionChanged event; some cases are served just fine by requesting the position directly only when it is needed. Each application has different needs in terms of how frequently to update position information and whether to individually request it with the GetGeopositionAsync method or use change events.

Working with Geocoordinate Values

An instance of the Geoposition class is returned both from a call to GetGeopostionAsync and within the Position member of the PositionChangedEventArgs event arguments that are provided to PositionChanged event handlers. Although the Geoposition class contains both Coordinate and CivicAddress properties, the CivicAddress values are not populated in Windows 8.1 (the only member that is set is the Country property, which is obtained from the country value set in the Windows region settings instead of the location information data sources that were previously mentioned). The Coordinate property is an instance of the Geocoordinate class and contains several different kinds of position information that are returned either from the Windows Location Provider or from GPS sensors (as you have seen, this depends on how the Geolocator is configured).

At its root, the Geocoordinate object provides a PositionSource property that either indicates how the location information was obtained or includes a value of Unknown if information about the source is not available. It also include an Accuracy property that indicates how accurate (in meters) the latitude and longitude position information are believed to be. If the location information is being obtained from a GPS sensor, values for the Speed, Heading, AltitudeAccuracy, and SatelliteData properties might also be included, depending on the sensor’s capabilities.

The actual position information provided is a little buried in the object hierarchy. It is actually returned in the Position property within the Point property of the Geocoordinate instance. Regardless of whether the location information is obtained from a GPS sensor or the Windows Location Provider, values for Latitude and Longitude (measured in degrees) are provided in this Point property. If the information is obtained from a GPS sensor, the Altitude value might be provided as well.

To show how this information looks in practice, the example application includes the capability to display all the fields of the Geocoordinate object for the current location. Clicking the ShowCurrent button in the app’s Location box displays a pop-up that contains these values, as provided by a call to the GetGeoPositionAsync method. The Center Map on Current button also makes a call to the GetGeopositionAsync method and sets a viewmodel property from the previously discussed Point property. The property in the ViewModel is data bound to the Bing Maps control so that when the value changes, the map centers itself at the Latitude and Longitude coordinates specified in the position value.

Using the Simulator Location Tools

Chapter 2 introduced you to the Visual Studio simulator for Windows Store Apps (the Simulator), which enables you to run and test your Windows 8.1 app within a simulated environment on your development system. In addition to being able to emulate various screen sizes and resolutions (along with the other functionality it provides), the simulator can be used to provide simulated geolocation values to a running app, which can help you test your location-aware app. To use the simulator’s location functions, several requirements must be met, primarily related to Location Settings enabled on the local system. When you first try to use the location functions, you are prompted and instructed to take corrective action if your system does not meet the necessary requirements for the location simulator to run.

To use the location functions of the simulator with your location-aware app, start debugging in the simulator following the instructions in Chapter 2. When the app is running in the simulator, clicking the icon in the simulator toolbar that resembles a globe displays the location simulation dialog box (see Figure 13.5). When the Use Simulated Location check box is checked, it provides access to text boxes for setting location values such as Latitude, Longitude, and Altitude.

FIGURE 13.5

FIGURE 13.5 Using location tools in the Windows simulator

When the Set Location button is clicked, the Geolocator API methods and events that are used by the app running in the simulator use those values for their position information. Removing the check from the Use Simulated Location check box returns the simulator to using the host system values for its current position values.

Geofencing

Geofencing enables your Windows 8.1 app to define geographic boundaries (known as geofences) and monitor a device’s position relative to those boundaries. Your app then produces notification events when the device enters or exits those boundaries. For applications that need to be alerted when a device has moved into or beyond one of these boundaries, this provides a much more efficient solution than polling the geolocation APIs and making the determinations programmatically. To support geofencing, the Windows Runtime includes APIs that allow registering and managing geofences, as well subscribing to and processing the related notifications.

Getting Started

The geofencing support the Windows Runtime provides is closely related to the geolocation support and includes several of the same restrictions and conditions related to working with personally identifiable information. To use geofencing, you must set the Location capability in the app manifest and set both the app-specific and system-wide permission settings to allow the app to access location information.

The GeofenceMonitor class provides geofencing support in the Windows Runtime. Unlike using the Geolocator, where you create a new instance of the class to access the functionality, a reference to the GeofenceMonitor is accessed through its static Current property:

var geofenceMonitor = GeofenceMonitor.Current;

The following sections discuss the functionality that the GeofenceMonitor exposes.

Defining a Fence

The GeofenceMonitor works with a collection of Geofence instances. Each Geofence object describes the region the fence covers, the types of events to provide, and the conditions under which it indicates that an event has occurred. Table 13.4 describes the settings provided by the Geofence class. Be aware that these values must be set through one of the Geofence constructors and cannot be changed after the geofence has been defined.

TABLE 13.4 Geofence Settings

Setting

Description

Id

Specifies the ID for the fence. The ID is a String that must be unique within the scope of the current app, and it must be a maximum of 64 characters long. This value is required.

Geoshape

Specifies the geofence boundary. Currently supports being set to only a Geocircle instance, which defines the boundary via a center point and a radius. This value is required.

MonitoredStates

Specifies which events the GeofenceMonitor raises for this fence. Can be set to a combination of the MonitoredGeofenceStates enumeration values, which includes Entered, Exited, and Removed, but must minimally include either Entered or Exited. This value is optional and, by default, is set to the combination of Entered and Exited.

SingleUse

Specifies whether the fence is automatically removed. If set, when each of the MonitoredStates (with the exception of Removed) is reached at least once, the fence is automatically removed from the GeofenceMonitor collection. This value is optional and, by default, is set to false.

DwellTime

Specifies the time that must elapse when a geofence condition is met before an event is raised. This value is optional and, by default, is set to 10 seconds.

StartTime

Specifies the time at which the geofence monitoring begins. This value is optional and, by default, is set to a minimal value of January 1, 1601 (which is the base value for the Windows FILETIME structure).

Duration

Specifies the amount of time following StartTime during which the fence should be monitored. This value is optional and, by default, is set to TimeSpan.Zero, which indicates an indefinite duration.

After a Geofence instance has been defined, the GeofenceMonitor begins tracking it when it is added to its Geofences collection.

In the example app, the code for working with the GeofenceMonitor has been consolidated into the GeofenceHelper class. This class provides an AddGeofence method used to add new fences (see Listing 13.9). Clicking the Add Fence at Map Center button in the example application produces a flyout that enables the user to define a name for the geofence. The center point is retrieved from the map’s current center point, and the radius is hardcoded to 20KM. Pressing the flyout’s Add Fence button calls this method with the values in the flyout. This method then creates a Geocircle with the specified center and radius, which is provided to the Geofence constructor along with indications that the GeofenceMonitor should listen to Entering, Exited, and Removed events (the next section covers the events) and that the geofence is not configured to be single use. Default values are accepted for the remaining parameters. The resulting Geofence instance is then added to the GeofenceMonitor collection and returned so that it can be used to include a UI entry on the Bing Maps control.

LISTING 13.9 Adding a Geofence

public Geofence AddGeofence(
    String fenceId,
    BasicGeoposition fenceCenter,
    Double radiusInMeters)
{
    var fenceCircle = new Geocircle(fenceCenter, radiusInMeters);

    const MonitoredGeofenceStates states =
        MonitoredGeofenceStates.Entered |
        MonitoredGeofenceStates.Exited |
        MonitoredGeofenceStates.Removed;

    // Create the fence with the desired states and not single-use
    var fence = new Geofence(fenceId, fenceCircle, states, false);
    GeofenceMonitor.Current.Geofences.Add(fence);
    return fence;
}

When defining a geofence boundary, keep in mind the limitations of the accuracy of the various location providers available to the Windows Runtime. Depending on sensor capabilities and network connectivity, extremely small fences might not be all that useful.

Geofence Events

You can receive notifications that geofencing events have occurred in two ways. Foreground notifications are configured when an app registers an event handler for the GeofenceStateChanged event provided by the GeofenceMonitor class. Alternatively, you can set up a background task to process geofence notifications even when the app is not running in the foreground. To configure geofencing background task notifications, the LocationTrigger class needs to be provided to a BackgroundTaskBuilder, and that builder instance needs to be configured and then registered. Chapter 15, “Background Tasks,” covers this in more detail.

As previously discussed, the GeofenceMonitor events can be triggered in response to the device entering or exiting a geofence, depending on the combination of the Entered or Exited GeofenceState enumeration values provided in the MonitoredStates value when the Geofence instance was defined. Additionally, the event can occur in response to the geofence being automatically removed from the list of monitored fences and depending on whether the Removed enumeration value was specified.

Automatic removal of a Geofence occurs in response to the values set in its Duration and SingleUse properties. Duration is the easiest to understand. When the time window indicated by the combination of the StartTime and Duration properties has expired, a geofence event is recorded indicating that this fence is no longer being monitored. In this case, the event includes a RemovalReason value that is set to Expired.

The other option for automatic removal relates to the SingleUse property. When this value is set to true, a geofence is removed after all its MonitoredStates have occurred. If a Geofence instance is defined with only Entered or Exited specified, then as soon as the corresponding event takes place, the geofence is removed. If both Entered and Exited are specified, the geofence is removed only after both have occurred. In this case, the Removed state is accompanied by a RemovalReason value of Used.

When a geofence notification event is received, the app should call the ReadReports method of the GeofenceMonitor instance, which returns the collection of all notification reports that have accumulated since the last call to ReadReports was made. Each report is actually indicated in an individual GeoStateChangedEventReport, and a single GeofenceMonitor event can encompass multiple reports, especially in the case of background tasks, which run only periodically.

The example app subscribes to geofence notifications only in the foreground. To do so, the GeofenceHelper class registers its HandleGeofenceStateChanged method as a handler for the GeofenceStateChanged event (see Listing 13.10).

LISTING 13.10 Processing Geofence Events

private void HandleGeofenceStateChanged(GeofenceMonitor monitor,Object o)
{
    // Iterate over and process the accumulated reports
    var reports = monitor.ReadReports();
    foreach (var report in reports)
    {
        switch (report.NewState)
        {
            case GeofenceState.Entered:
            case GeofenceState.Exited:
                var updateArgs = new FenceUpdateEventArgs
                {
                    FenceId = report.Geofence.Id,
                    Reason = report.NewState.ToString(),
                    Timestamp = report.Geoposition.Coordinate.Timestamp,
                    Position =
                            report.Geoposition.Coordinate.Point.Position
                };
                OnFenceUpdated(updateArgs);
                break;
            case GeofenceState.Removed:
                var removedArgs = new FenceRemovedEventArgs
                {
                    FenceId = report.Geofence.Id,
                    WhyRemoved = report.RemovalReason.ToString()
                };
                OnFenceRemoved(removedArgs);

                break;
        }
    }
}

The event handler retrieves the reports from the provided GeofenceMonitor instance and then iterates over the individual report instances. In the case of Entered and Exited events, information is gathered about which fence caused the event, whether it was triggered on enter or exit, what position caused the event to be triggered, and when exactly the reported event occurred. This information is then used to relay an event out of the GeofenceHelper that displays the event’s occurrence in the app UI. In the case of a Removed event, the event ID and Removal reason are obtained, and a similar event is raised to provide notification as well as remove the geofence entry from the Bing Maps control.

Be aware that because the GeofenceStateChanged events are raised from an external entity, the handler will not run on the UI thread. Any reaction to these events that affects the application UI needs to be marshalled to the proper thread using either the Dispatcher or a valid SynchronizationContext, as discussed in the section “Accessing the UI Thread” in Chapter 9, “Model-View-ViewModel.”

Managing Geofences

You can manage geofences by working directly with the Geofences collection that the GeofenceMonitor instance provides. The example app enumerates these instances in two places. First, on app startup, the existing collection is obtained to put markers on the Bing Maps control for each geofence. Second, clicking the List Fences button shows a flyout that lists all the currently defined geofences. This flyout includes the option to remove the selected Geofence instance from the Geofences collection. Note that programmatically removing a fence from the collection in this way does not generate the previously discussed Removed events. Those occur only when the removal happens automatically in response to the conditions that the Geofence instance’s settings identify.

Motion and Orientation Sensors

In addition to using the Geolocator and related APIs to obtain information about a device’s physical location, the Windows Runtime provides APIs for interacting with a class of sensors related to the movement and positioning of the device itself. Table 13.5 lists the kinds of sensors these APIs can interact with and the kind of data they gather.

TABLE 13.5 Motion and Orientation Sensor Types

Sensor

Description

Simple Orientation

Reports the current orientation of the device based on values from the SimpleOrientation enumeration.

Compass

Provides information about the position of the device in relation to magnetic north. This is actually a composite sensor whose output is based on combined input from magnetometer and gyrometer sensors.

Inclinometer

Provides information about the pitch, yaw, and roll state of a device. This is a composite sensor whose output is based on combined input from accelerometer, gyrometer, and magnetometer sensors.

Accelerometer

Provides information about the G-forces affecting the device’s x-, y-, and z-axes.

Gyrometer

Provides information about the angular velocity along the device’s x-, y-, and z-axes.

Orientation Sensor

Provides detailed information about how a device is situated in space. This is a composite sensor whose output is based on combined input from accelerometer, gyrometer, and magnetometer sensors.

Light Sensor

Provides information about the amount of light currently striking the device display.

You might have noticed that several of these sensors’ values are determined in part from a magnetometer, which is itself a sensor whose purpose is to measure the strength of magnetic fields. However, the Windows Runtime does not provide any APIs that allow direct access to output from magnetometers.

For the most part, the API for interacting with sensors is similar across all the different kinds. They all basically offer the capability to obtain a reference to a class instance that provides access to the sensor, as well as methods for obtaining the current sensor value. In addition, they provide events that you can subscribe to for notifications when the value changes. The majority of the sensor APIs define properties that specify the minimum interval with which the sensor can raise these change events, as well as properties that specify the requested interval for reporting value changes.

Most of the code for working with sensors in the example project resides in the SensorHelper class. You might be relieved to know that, unlike the location information, the information these sensors return is not considered to be personally identifiable information. As a result, you do not have to indicate entries in the application manifest, prompt the user for permission, or deal with users blocking access to the sensors if you include code to make use of them in your application.

Simple Orientation Sensor

The simple orientation sensor is the simplest of the available sensors. It does not work with the concept of a reporting interval for its change events, and the data values that it reports are simply members of the SimpleOrientation enumeration. When available, the purpose of this sensor is to describe which way the device is facing. The values it can return are NotRotated, for when the device is sitting in a “natural” landscape orientation; Rotated90, Rotated180, and Rotated270, to indicate that the device has been rotated to stand on one of its other edges; and FaceUp and FaceDown, to indicate that the device is lying flat.

The code in Listing 13.11 shows how the example project is configured to work with the simple orientation sensor, which is exposed via the SimpleOrientationSensor class. The GetDefault static method obtains a reference to the sensor, the value of which is null if the sensor is not available. After that, it simply provides a handler for the OrientationChanged event and then uses the GetCurrentOrientation method to obtain the current sensor value.

LISTING 13.11 Configuring the Simple Orientation Sensor

// Get the reference to the sensor and see if it is available
_simpleOrientation = SimpleOrientationSensor.GetDefault();
if (_simpleOrientation == null) return;

_sensorSettings.IsSimpleOrientationAvailable = true;

// NOTE - Simple Orientation does not offer a minimum interval setting
_simpleOrientation.OrientationChanged
    += SimpleOrientationOnOrientationChanged;

// Read the initial sensor value
_sensorSettings.LatestSimpleOrientationReading
    = _simpleOrientation.GetCurrentOrientation();

The Visual Studio simulator for Windows Store Apps, which the preceding section “Using the Simulator Location Tools” discussed, also includes support for simulating device rotation by providing buttons that rotate the simulator in 90-degree increments clockwise or counterclockwise.

Compass

The compass provides information about the current heading of the device relative to magnetic north. When available, this sensor returns readings as instances of the CompassReading type, which includes both HeadingMagneticNorth and HeadingTrueNorth properties, indicating degrees to magnetic north and degrees to true north, respectively. HeadingMagneticNorth always is provided; the availability of HeadingTrueNorth values depends on the individual capabilities of the actual sensor hardware. HeadingTrueNorth returns a value of null if it is not available.

Listing 13.12 shows how the example project is configured to work with the compass, which is exposed via the Compass class. The GetDefault static method obtains a reference to the sensor, the value of which is null if the sensor is not available. It next proceeds to set the sensor’s reporting interval.

LISTING 13.12 Configuring the Compass

// Get the reference to the sensor and see if it is available
_compass = Compass.GetDefault();
if (_compass == null) return;

_sensorSettings.IsCompassAvailable = true;

// Set the minimum report interval. Care must be taken to ensure
// it is not set to a value smaller than the device minimum
var minInterval = _compass.MinimumReportInterval;
_compass.ReportInterval
    = Math.Max(_sensorSettings.SensorReportInterval, minInterval);
_compass.ReadingChanged += CompassOnReadingChanged;

// Read the initial sensor value
_sensorSettings.LatestCompassReading = _compass.GetCurrentReading();

The ReportInterval property is common to most of the available sensors. The purpose of the property is to provide access to the minimum time (in milliseconds) that must elapse between ReadingChanged events. Take care when setting this value; setting it to a value below the minimum value that the sensor can support can result in either an exception or unpredictable behavior, depending on the sensor. You can obtain the minimum allowable report interval value through the MinimumReportInterval property. Note that the ReportInterval setting has some of the characteristics of a request rather than a certain value. Several factors can influence how the actual sensor handles the ReportInterval setting. For example, when other apps on the system that make use of the same sensor set their own values for this property, the sensor might simply elect to use whichever is the smallest defined value. Also be aware that the ReadingChanged event is raised only when the reading actually changes, regardless of the ReportInterval setting. It is important to not confuse the ReportInterval value with a frequency value that somehow guarantees that the ReadingChanged event will be raised repeatedly in a steady cadence. After the ReportInterval is set, the code simply provides a handler for the ReadingChanged event and then uses the GetCurrentReading method to obtain the current sensor value.

Another important note is that the value the compass returns is relative to the device being in a regular landscape orientation, with the device base sitting at the bottom. (If the device is a tablet device built with Portrait as its primary orientation, this sensor landscape condition still applies; the “natural” landscape mode is the one where the hardware Windows button ends up on the right side of the display.) FIGURE 13.6 shows devices in natural landscape orientation.

FIGURE 13.6

FIGURE 13.6 Devices in natural landscape orientation

If the device is in a different orientation, the value the sensor returns needs to be adjusted to account for this. The example project includes a CompassOffset extension method for the DisplayOrientations class that you can use to obtain the offset to apply to a compass direction based on a provided orientation value. This method simply returns a value of 0, 90, 180, or 270, depending on what is needed to correct the compass reading for the given orientation. You obtain the DisplayOrientations value to use from the CurrentOrientation property of the DisplayInformation class. After you determine the offset, you can add it to the HeadingMagneticNorth or HeadingTrueNorth values, using modular arithmetic to constrain the resulting value between 0 and 360 degrees, as follows:

(LatestCompassReading.HeadingMagneticNorth + offset)%360

The example project includes a Sensor Settings flyout that you can bring up using the Settings Charm. The panel includes a slider for updating the minimum reporting interval for the sensors. It also includes a check box that corresponds to a flag that the app uses to decide whether to compensate for orientation changes when using and displaying sensor values. By toggling these values and switching the orientation of the device on which the app is running from a landscape to an inverted landscape orientation, you can see the effect that changing an orientation has on sensor values, as well as how the compensation code will correct them to their expected state.

Another feature present in the example app is the capability for the Bing Maps control to “follow” the compass sensor value. Note that the current version of the Bing Maps control supports rotating its display contents only when viewed at high zoom levels (and to only one of four discrete views), so this behavior is best viewed when the map is set to display and is zoomed in enough to show bird’s-eye imagery. To enable this feature, check the Follow box in the Compass panel in the app, and then point the device in different directions. When the Follow box is checked, the Tick event handler for a timer on the display page periodically polls the SensorSettings class for the LatestCompassReading value, which is set by the ReadingChanged handler for the compass. This value then is set to a viewmodel property that is data bound to the Bing Maps control. This approach of using a timer to check for the most recent value is used because the ReadingChanged event is fired only when a compass value actually changes, as previously discussed. Listing 13.13 shows the code in the timer event handler that obtains and applies the compass value.

LISTING 13.13 Applying the Compass Orientation to the Map Display

if (_sensorSettings.IsFollowingCompass)
{
    // Get the latest compass reading
    var compassReading = _sensorSettings.LatestCompassReading;

    // Adjust the reading based on the display orientation, if necessary
    var displayOffset = _sensorSettings.CompensateForDisplayOrientation
        ? _sensorSettings.DisplayOrientation.CompassOffset()
        : 0;
    var heading
        = (compassReading.HeadingMagneticNorth + displayOffset)%360;

    // Set the value used by data binding to update the map's heading
    DefaultViewModel["Heading"] = heading;
}

Inclinometer

The inclinometer provides information about the current pitch, yaw, and roll of the device. Pitch represents the degrees of rotation around the x-axis, yaw represents degrees of rotation around the z-axis, and roll represents degrees of rotation around the y-axis. FIGURE 13.7 illustrates how these values map to the physical position of a tablet device. When available, this sensor returns readings as instances of the InclinometerReading type, which provides its results in PitchDegrees, RollDegrees, and YawDegrees properties.

FIGURE 13.7

FIGURE 13.7 Pitch, roll, and yaw relative to a tablet device

Listing 13.14 shows how the example project is configured to work with the inclinometer, which is exposed via the Inclinometer class. The steps involved in configuring the inclinometer are basically identical to those shown in Listing 13.12 for configuring the compass.

LISTING 13.14 Configuring the Inclinometer

// Get the reference to the sensor and see if it is available
_inclinometer = Inclinometer.GetDefault();
if (_inclinometer == null) return;

_sensorSettings.IsInclinometerAvailable = true;

// Set the minimum report interval. Care must be taken to ensure
// it is not set to a value smaller than the device minimum
var minInterval = _inclinometer.MinimumReportInterval;
_inclinometer.ReportInterval
    = Math.Max(_sensorSettings.SensorReportInterval, minInterval);
_inclinometer.ReadingChanged += InclinometerOnReadingChanged;

// Read the initial sensor value
_sensorSettings.LatestInclinometerReading = GetInclinometerReading();

Much like the compass, the values the inclinometer returns are relative to the device being in a regular landscape orientation, and the resulting values also need to be normalized if the device is being used from any other orientation. The example project includes an AxisAdjustmentFactor extension method for the DisplayOrientations class that you can use to obtain the factors to apply to the x-, y-, and z-axis results, based on the current device orientation.

The example app includes a fun feature that you can enable by checking the Follow box in the Inclinometer panel in the app. When this box is checked, the content of Bing Maps control slides based on the Inclinometer readings, allowing you to navigate the map simply by tilting your device back and forth or left and right.

As with the Follow feature discussed previously for the compass sensor, implementation for this feature simply polls the SensorSettings class in response to the same timer Tick event. In this case, the value used to obtain the current device orientation is the LatestInclinometerReading value, which the inclinometer’s ReadingChanged handler sets. The displayAdjustment value used to compensate for device orientation changes returns per-axis values of +1 or –1 that are multiplied to the sensor result to normalize the value.

Listing 13.15 shows the calculations that move the map. First, the inclinometer reading is obtained and normalized, depending on the value of the compensation setting and the device orientation. Next, a rate of one full screen per timer tick was found to be a good maximum rate of traversal, so the number of x- and y-axis pixels to move are obtained from the map control. Then trigonometric functions convert the adjusted pitch and roll values to percentage values so that the traversal is nearly nothing when the device is lying flat and is full-value when it is held vertically. This percentage determines the actual number of x and y pixels to move in the current tick, which is applied to the center point to determine the equivalent destination point. From here, the Bing Maps TryPixelToLocation utility function converts a pixel onscreen to equivalent latitude and longitude values, which then set the new map position.

LISTING 13.15 Applying the Inclinometer Reading to the Map Display

if (_sensorSettings.FollowInclinometer)
{
    var inclinometerReading = _sensorSettings.LatestInclinometerReading;

    // Optionally normalize the sensor reading values
    var displayAdjustment
        = _sensorSettings.CompensateForDisplayOrientation
            ? _sensorSettings.DisplayOrientation.AxisAdjustmentFactor()
            : SensorExtensions.AxisOffset.Default;
    var adjustedPitchDegrees
        = inclinometerReading.PitchDegrees * displayAdjustment.X;
    var adjustedRollDegrees
        = inclinometerReading.RollDegrees * displayAdjustment.Y;

    // At full speed/inclination, move 100% map size per tick
    const Double maxScreensPerTick = 1.00;
    var mapWidth = ExampleMap.ActualWidth;
    var xFullRateTraversalPerTick = mapWidth * maxScreensPerTick;
    var mapHeight = ExampleMap.ActualHeight;
    var yFullRateTraversalPerTick = mapHeight * maxScreensPerTick;

    // Turn rotation angles into percentages
    var xTraversalPercentage
        = Math.Sin(adjustedRollDegrees*Math.PI/180);
    var yTraversalPercentage
        = Math.Sin(adjustedPitchDegrees*Math.PI/180);

    // Compute the final traversal amounts based on the percentages
    // and compute the new destination center point
    var xTraversalAmount
        = xTraversalPercentage*xFullRateTraversalPerTick;
    var yTraversalAmount
        = yTraversalPercentage*yFullRateTraversalPerTick;
    var destinationPoint = new Point(
        mapWidth/2 + xTraversalAmount,
        mapHeight/2 + yTraversalAmount);

    // Use the Bing Maps methods to convert pixel pos to Lat/Lon
    // rather than trying to figure out Mercator map math
    Location location;
    if (ExampleMap.TryPixelToLocation(destinationPoint, out location))
    {
        // Obtain the current map position (for altitude)
        var position = (BasicGeoposition)DefaultViewModel["Position"];

        var newPosition = new BasicGeoposition
        {
            Altitude = position.Altitude,
            Latitude = location.Latitude,
            Longitude = location.Longitude
        };

        DefaultViewModel["Position"] = newPosition;
    }
}

Accelerometer

The accelerometer provides information about the current G-forces acting on the device in the x, y, and z directions. At rest, the most significant G-force affecting a device is the force of gravity, which pulls down along whichever axis corresponds to the bottom edge of the device with a value of –1.0. For example, if a device is standing up on its bottom edge in a landscape profile, the y value has a value of approximately –1.0. When available, this sensor returns readings as instances of the AccelerometerReading type, which provides its results in AccelerationX, AcclerationY, and AccelerationZ properties.

Listing 13.16 shows how the example project is configured to work with the accelerometer, which is exposed via the Accelerometer class. The steps involved in configuring the accelerometer are otherwise identical to those shown previously for configuring the other sensors, with one notable exception. The accelerometer sensor includes an additional Shaken event that is raised when the sensor detects that the device is being subjected to several quick back-and-forth motions.

LISTING 13.16 Configuring the Accelerometer

// Get the reference to the sensor and see if it is available
_accelerometer = Accelerometer.GetDefault();
if (_accelerometer == null) return;

_sensorSettings.IsAccelerometerAvailable = true;

// Set the minimum report interval. Care must be taken to ensure
// it is not set to a value smaller than the device minimum
var minInterval = _accelerometer.MinimumReportInterval;
_accelerometer.ReportInterval
    = Math.Max(_sensorSettings.SensorReportInterval, minInterval);
_accelerometer.ReadingChanged += AccelerometerOnReadingChanged;
_accelerometer.Shaken += AccelerometerOnShaken;

// Read the initial sensor value
_sensorSettings.LatestAccelerometerReading = GetAccelerometerReading();

Gyrometer

The gyrometer provides information about the device’s current rate of rotation around the x-, y-, and z-axes, measured in degrees per second. When available, this sensor returns readings as instances of the GyrometerReading type, which provides its results in AngularVelocityX, AngularVelocityY, and AngularVelocityZ properties.

Listing 13.17 shows how the example project is configured to work with the gyrometer, which is exposed via the Gyrometer class. The steps involved in configuring the gyrometer are otherwise identical to those shown previously for configuring the other sensors.

LISTING 13.17 Configuring the Gyrometer

// Get the reference to the sensor and see if it is available
_gyrometer = Gyrometer.GetDefault();
if (_gyrometer == null) return;

_sensorSettings.IsGyrometerAvailable = true;

// Set the minimum report interval. Care must be taken to ensure
// it is not set to a value smaller than the device minimum
var minInterval = _gyrometer.MinimumReportInterval;
_gyrometer.ReportInterval
    = Math.Max(_sensorSettings.SensorReportInterval, minInterval);
_gyrometer.ReadingChanged += GyrometerOnReadingChanged;

// Read the initial sensor value
_sensorSettings.LatestGyrometerReading = GetGyrometerReading();

Orientation Sensor

The last sensor directly related to motion and/or orientation to be discussed is the orientation sensor. As Table 13.4 described, the orientation sensor is a composite sensor whose output consists of information gathered from accelerometer, gyrometer, and magnetometer data. As you can see in Listing 13.18, the orientation sensor is configured using the OrientationSensor class in the same way the rest of the sensors have been in this section. Its results are returned in an instance of the OrientationSensorReading class, which contains properties for Quaternion and RotationMatrix values, structures that 3D and gaming apps often use.

LISTING 13.18 Configuring the Orientation Sensor

// Get the reference to the sensor and see if it is available
_orientationSensor = OrientationSensor.GetDefault();
if (_orientationSensor == null) return;

_sensorSettings.IsOrientationSensorAvailable = true;

// Set the minimum report interval. Care must be taken to ensure
// it is not set to a value smaller than the device minimum
var minInterval = _orientationSensor.MinimumReportInterval;
_orientationSensor.ReportInterval
    = Math.Max(_sensorSettings.SensorReportInterval, minInterval);
_orientationSensor.ReadingChanged += OrientationSensorOnReadingChanged;

// Read the initial sensor value
_sensorSettings.LatestOrientationSensorReading
    = GetOrientationSensorReading();

Light Sensor

The light sensor isn’t actually a motion-/orientation-related sensor, but it is included as an honorable mention with these sensors because the APIs for working with this sensor are closely related to the rest of the APIs in this section. The light sensor reports the intensity of the light shining on the current device display in units of lux, is accessed through the LightSensor class, and returns its values in a LightSensorReading instance (which contains the property IlluminanceInLux). Listing 13.19 shows how the example project is configured to work with the light sensor.

LISTING 13.19 Configuring the Light Sensor

// Get the reference to the sensor and see if it is available
_lightSensor = LightSensor.GetDefault();
if (_lightSensor == null) return;

_sensorSettings.IsLightSensorAvailable = true;

// Set the minimum report interval. Care must be taken to ensure
// it is not set to a value smaller than the device minimum
var minInterval = _lightSensor.MinimumReportInterval;
_lightSensor.ReportInterval
    = Math.Max(_sensorSettings.SensorReportInterval, minInterval);
_lightSensor.ReadingChanged += LightSensorOnReadingChanged;

// Read the initial sensor value
_sensorSettings.LatestLightSensorReading = GetLightSensorReading();

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020