There are a wide and growing variety of sensors that can be found on an Android handset, from accelerometers and gyroscopes to light and proximity sensors. Most of these devices can be accessed with the android.hardware.SensorEvent class, although naturally they each produce their own specific data sets.
Here we will use Sensor.TYPE_ACCELEROMETER to measure a handset's motion in three dimensions before going on to explore other sensor types.
Getting ready
Gathering information from sensors is quite straightforward as Android provides a handy interface, android.hardware.SensorEventListener, to facilitate this. Nevertheless there is a little more housekeeping required than previous tasks as we must take control of the registering of these listeners with the SensorManager class.
Start up a new Android project in Eclipse and create a TextView with an ID in main.xml.
How to do it...
1. Inside our main activity's Java class edit the declaration so that it implements SensorEventListener, like so:
public class MotionDetector extends Activity implements SensorEventListener {
2. Just below this, declare a class field of type SensorManager:
private SensorManager mSensorManager;
3. At the end of the onCreate() method add the following SensorManager assignment:
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
4. We need to disable our sensors when we are not using them, so add an onPause() and an onResume() method, completed as seen here:
@Override
protected void onResume() { super.onResume();
mSensorManager.registerListener(
this,
mSensorManager.getDefaultSensor(
Sensor.TYPE_ACCELEROMETER ),
SensorManager.SENSOR_DELAY_UI);
}
@Override
protected void onPause() { super.onPause();
mSensorManager.unregisterListener(this);
}
5. Eclipse will probably have informed you of an error by this point and will offer to add the unimplemented methods. Accept this suggestion and fill out the body of the onSensorChanged() method as follows:
public void onSensorChanged(SensorEvent e) { synchronized (this) {
if (e.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { mTextView.setText("x= " + e.values[0] +
"\ny= " + e.values[1] + "\nz= " + e.values[2]);
} } }
6. The onAccuracyChanged() method is not used in this example but must be included anyway—you can leave it like this:
public void onAccuracyChanged(Sensor sensor, int accuracy) { // TODO Auto-generated method stub
}
7. Provided you have assigned the TextView with findViewById(), this program can be run on any emulator or handset with an accelerometer:
How it works...
Reading a sensor's values requires several components, including the SensorEventListener interface that implements two callbacks which we use to respond to changes in sensor values or accuracy. The onAccuracyChanged() callback is required less often but is nevertheless useful as demands on battery and environmental conditions can cause this setting to change.
The possible constant int values for this can be:
SENSOR_STATUS_ACCURACY_HIGH, SENSOR_STATUS_ACCURACY_MEDIUM SENSOR_STATUS_ACCURACY_LOW
Many sensors, including the accelerometer, are a powerful drain on a device's battery so we need to disable them when not in use. We did so in the onPause() callback using SensorManager's unregisterListener(SensorEventListener) method.
Registering the listener, on the other hand, was slightly more complex, requiring a SensorEventListener, an int type, which here was the default accelerometer, and a delay value. This delay (int) value controls how quickly the sensor operates and can have a dramatic affect on power usage. There are four settings:
f SENSOR_DELAY_FASTEST
f SENSOR_DELAY_GAME
f SENSOR_DELAY_NORMAL
f SENSOR_DELAY_UI
The actual reading of the accelerometer's values was done in the onSensorChanged() method. We managed the SensorEvent's sensor.getType() method to select the accelerometer and we can also use this to gain other information about a sensor such as sensor.getPower(), which returns the power the sensor uses in micro amps or sensor.
getMaximumRange(), which returns a distance based on the sensor's own measurements.
The accelerometer, along with the magnetic field sensor and the gyroscope, returns three values based on a coordinate system. In our example here the three values describe the acceleration of the device along each of the three axes of this coordinate system measured in m/s2. Relative to the handset the three directions are as shown in the next image:
Note that the force of Earth's gravity (approximately 9.8 m/s2) will always register with the accelerometer. For example, if a phone is perfectly upright, as in the diagram, then the Y axis, event.values[1], will read -9.8.
There's more...
The technique we used to register and access the accelerometer is very similar in operation and structure to the way we would access the other sensors.
Accessing any available sensor
We referred to our accelerometer with the Sensor constant TYPE_ACCELEROMETER in the two lines:
We could have used any one of several constants provided by the Sensor object. Here is a comprehensive list of sensor types:
f TYPE_ACCELEROMETER
These are largely self explanatory with the possible exception of TYPE_ALL. This special case is used to detect any sensor.
Listing available sensors
Android handsets come with a wide variety of sensors but which sensors are included is a matter for manufacturers to decide and differs from model to model. As developers we need some way to detect which sensors are available to us. In particular we may want to select between sensors that perform similar functions. For example it may be preferable to measure motion with a gyroscope if one is available but prepare a function that utilizes the accelerometer when one is not.
Getting ready
The project we put together in the last recipe is as good a place to start this exercise as any, as we will be doing little in the way of coding. You can of course apply this task to any of your own applications if you prefer.
How to do it...
1. Open up the Java activity file and declare the following private members:
private SensorManager mSensorManager;
private TextView mTextView;
private List mList;
2. You will have needed to define the TextView in XML and provide it with an android:id. 3. Inside the onCreate() method assign our SensorManager and TextView like this:
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mTextView = (TextView) findViewById(R.id.text_view);
4. Finally, add the following statement block just beneath this:
mList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
for (int i = 1; i < mList.size(); i++) { mTextView.append("\n" + mList.get(i));
}
5. When run on a handset or emulator this routine will display a list of all available sensors for that device:
How it works...
This is a nice simple exercise and all we have done is demonstrate the usefulness of SensorManager's getSensorList(int) method. In practice we would probably only make inquiries regarding a particular sensor type and this is simply a matter of replacing the Sensor.TYPE_ALL constant with the appropriate type in the mSensorManager.
getSensorList() call.
Being able to test for the presence of sensors enables us to develop for a wide variety of handsets without necessarily knowing in advance what hardware is available.