Just as touching a device screen is input, so is rotating and moving the actual physical device in its environment. Most devices are built with several sensors that can be used to read the state of the device in its environment.
We can access all the available sensors and the sensor data on the device through the SensorManager
type:
SensorManager
type is obtained from the current context:var manager = SensorManager.FromContext(this);
GetDefaultSensor()
method:var type = SensorType.Accelerometer; var accelerometer = manager.GetDefaultSensor(type); if (accelerometer == null) { // handle no acceleromenter }
ISensorEventListener
interface:public class MyActivity : Activity, ISensorEventListener { public void OnAccuracyChanged( Sensor sensor, SensorStatus accuracy) { } public void OnSensorChanged(SensorEvent e) { } }
RegisterListener()
method:manager.RegisterListener( this, accelerometer, SensorDelay.Fastest);
manager.UnregisterListener(this);
The data from sensors can be used for many different purposes, ranging from making decisions to drawing on the screen. For example, we can make use of the accelerometer data in the OnSensorChanged()
method to create a level or plumb line:
private float rotationX; private float rotationY;
const float Filter = 0.05f; var x = e.Values[0]; var y = e.Values[1]; rotationX = (x * Filter) + (rotationX * (1f - Filter)); rotationY = (y * Filter) + (rotationY * (1f - Filter));
var rotation2d = (float)Math.Atan2(rotationY, rotationX); var degrees = -rotation2d * (180f / (float)Math.PI);
levelView.Rotation = degrees + 90f;
Many devices are built with multiple sensors. This can range from accelerometers and gyroscopes to proximity sensors and step counters. All of these sensors constantly stream data of where and how the device is situated in the environment. All this data can be used to both enhance as well as provide core functionality to an app.
For example, the accelerometer and gyroscope can be used to create a game that allows the user to control a player based on how the device is rotated. Or, the step counter sensor can be used to provide data to a health or fitness app.
However, not all devices have all the sensors. Before a sensor can be used by our app, we need to ensure that it exists on the device and that we can access the data. This is done using the SensorManager
instance. Once we have obtained the instance of the sensor manager, we are able to query the existence of a particular sensor using the GetDefaultSensor()
method.
The GetDefaultSensor()
method returns either the sensor that was requested or null
if the device does not support that sensor. As sensors are queried by type, there may be multiple versions of a sensor by different vendors. If this is the case, we can use the GetSensorList()
method instead. This will provide a collection of sensors that match the requested sensor type.
We can find out about a particular sensor using the various properties on the Sensor
type. These properties include the Version
and Vendor
properties, as well as the characteristics of the sensor, such as Power
for the power requirements.
Once we have the desired sensor, we can start reading the data stream. To do this, we need to implement the ISensorEventListener
interface, and register it with the sensor manager. Registering for data requires that we provide the sensor manager with the listener, sensor, and sampling rate of the RegisterListener()
method.
After the listener is registered, we will receive the sensor data in the OnSensorChanged()
method. This method receives the data from the sensor, which includes the accuracy, time, and the actual data values. The values from the sensor are provided via the Values
property on the SensorEvent
type. This property is an array of type float
, and the elements vary across sensor types.
For example, the accelerometer sensor has three elements, the first being the force along the x axis, the second being the force along the y axis, and the third being the force along the z axis. The step counter sensor only has one element, which is the number of steps taken since the device boot.
As the OnSensorChanged()
method may be invoked at a high rate, we should avoid blocking its return. If filtering or processing of the data is required, we should move it out from the current method and into another method. Also, when registering the listener, we can possibly select a lower rate of data if a high rate is not required. This will preserve both battery and system resources.
As having a listener registered to a sensor consumes battery, we should unregister the sensor as soon as it is no longer needed. We do this by passing the listener to the UnregisterListener()
method. If the sensor is used to update the UI, then as soon as the activity leaves the foreground, we can unregister it in the OnPause()
method and reregister it in the OnResume()
method.