Using Android’s Optional Hardware APIs
Interacting with Device Hardware
The Android platform allows unprecedented access to the device’s underlying hardware in a secure and robust manner. Because not all Android devices support or contain all hardware options, it is very important to follow these guidelines when accessing underlying device hardware: n Make no assumptions about the existence or availability of underlying hardware in code or otherwise. n Always check and verify optional features before trying to access hardware programmatically. n Pay special attention to exception handling as well as error and return value checking when working with hardware APIs. n Understand that hardware features are device resources.Acquire them late, and release them as soon as you’re done. In other words, play nice with the other apps. Don’t hog the hardware or drain the device battery by misusing hardware resources. Using Android’s Optional Hardware APIs 408 Chapter 19 Using Android’s Optional Hardware APIs Warning The Android emulator has very limited support for simulating hardware sensors, Wi-Fi, Bluetooth, and the device battery. These are cases when testing on real devices is crucial. Much of the code and APIs discussed in this chapter work only on Android hardware, and do little or nothing in the Android emulator. The optional hardware features of different Android devices are key market differentiators to consumers. For example, some might want a device that can act as a Wi-Fi hotspot. Others might require Bluetooth. Still others might be interested in the data that can be collected from various sensors on the device. Finally, applications can access data about the battery and the power management state.Also recall that we talked about other hardwarerelated features, such as the camera and location-based services, in Chapter 14,“Using Location-Based Services (LBS) APIs,” and Chapter 15,“Using Android Multimedia APIs,” respectively. Tip Many of the code examples provided in this chapter are taken from the SimpleHardware application. The source code for this application is provided for download on the book website.
Using the Device Sensor
The Android SDK provides access to raw data from sensors on the device.The sensors, and their precision and features, will vary from device to device. Some of the sensors that applications can interact with include the magnetic sensor, which can be used as a compass, and the accelerometer sensor that can detect motion. You can access the device sensors through the SensorManager object (android.hardware.SensorManager).The SensorManager object listens for data from the sensors. It is a system service, and you can retrieve an instance retrieved with the getSystemService() method, as shown here: SensorManager sensors = (SensorManager) getSystemService(Context.SENSOR_SERVICE); Working with Different Sensors The Sensor class (android.hardware.Sensor) defines a number of identifiers for the various sensors that you might find on a device. Not all sensors are available on each device.The most interesting sensors are listed here: n TYPE_ACCELEROMETER: Measures acceleration in three directions (values are SI units (m/s2 )) n TYPE_GYROSCOPE: Measures angular orientation in three directions (values are angles in degrees) n TYPE_ORIENTATION: Measures orientation in three directions (values are angles in degrees) (deprecated) Using the Device Sensor 409 n TYPE_LIGHT: Measures ambient light (values are SI lux units) n TYPE_MAGNETIC_FIELD: Measures magnetism in three directions; the compass (values are micro-Tesla (uT)) n TYPE_PRESSURE: Measures barometric pressure n TYPE_PROXIMITY: Measures the distance to an object (values in centimeters, or “near” vs.“far”) n TYPE_TEMPERATURE: Measures temperature The SensorManager class also has a number of constants that can be useful with certain sensors. For instance, you can use the STANDARD_GRAVITY constant with the accelerometer and the LIGHT_SUNLIGHT constant with the light sensor. Tip Not all sensors are available on all devices. For instance, the HTC Evo 4G Android handset has an accelerometer, magnetic sensor, and proximity sensor, but no temperature, pressure, or gyroscope sensors. Unfortunately, the emulator does not provide any sensor data. All sensor testing must be done on a physical device. Alternatively, OpenIntents.org also provides a handy Sensor Simulator (http://code.google.com/p/openintents/wiki/SensorSimulator). This tool simulates accelerometer, compass, and temperature sensors and transmits data to the emulator. Acquiring Access to a Sensor You can acquire access to a specific sensor using the SensorManager class method called getDefaultSensor().This method takes a sensor type parameter. For example, you could acquire the default accelerometer sensor as follows: Sensor accelSensor = sensors.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); Reading Sensor Data After you have a valid Sensor object, you can read the sensor data periodically. Sensor values are sent back to an application using a SensorEventListener object that the application must implement and register using the registerListener() method. boolean isAvailable = sensors.registerListener(SensorsActivity.this, accelSensor, SensorManager.SENSOR_DELAY_NORMAL); In this case, the accelerometer sensor is watched.The onSensorChanged() method is called at particular intervals defined by the delay value in registerListener(), which is the default value in this case. The SensorEventListener interface has two required methods you must implement: onAccuracyChanged() and onSensorChanged().The onAccuracyChanged() method is called whenever the accuracy of a given sensor changes.The onSensorChanged() method is called whenever the values of the sensor change.The onSensorChanged() method is generally used to inspect sensor information. 410 Chapter 19 Using Android’s Optional Hardware APIs Here is a sample implementation of onSensorChanged() that works for displaying various types of sensor data (not just the accelerometer): @Override public void onSensorChanged(SensorEvent event) { StringBuilder sensorMessage = new StringBuilder(event.sensor.getName()).append( » new values: « ); for (float value : event.values) { sensorMessage.append(« [« ).append(value).append(« ] »); } sensorMessage.append( » with accuracy « ).append(event.accuracy); sensorMessage.append( » at timestamp « ).append(event.timestamp); sensorMessage.append(« . »); Log.i(DEBUG_TAG, sensorMessage); } The onSensorChanged() method has a single parameter: a SensorEvent object. The SensorEvent class contains all the data about the sensor, including which sensor caused the event, the accuracy of the sensor, the sensor’s current readings and a timestamp. For details about what data to expect for each type of sensor, see the SensorEvent class documentation provided with the Android SDK. The accelerometer sensor provides three values corresponding to the acceleration minus Gravity on the x, y, and z axes. Output from a typical Android device with an accelerometer sensor is shown in Figure 19.1. Warning Depending on the sensor in use, the rate of sensor data might be very high. Be aware that your application should do as little as possible within the onSensorChanged() method.
Calibrating Sensors
The sensor values won’t be useful to the application until they are calibrated. One way to calibrate is to ask the user to click a button to calibrate the sensor.The application can then store the current values.Then new values can be compared against the original values to see how they have changed from their original values (delta).Although the phone sensors have a specific orientation, this enables the user to use the app in either portrait or landscape mode, regardless of how the user is holding the device. When registering a sensor, the registerListener() method returns true if the sensor is available and can be activated. It returns false if the sensor isn’t available or cannot be activated. The sensor values are typically quite sensitive. For most uses, an application probably wants to provide some smoothing of the values to reduce the effects of any noise or Using the Device Sensor 411 Figure 19.1 Sensor sample application showing accelerometer values. The orientation values might be appropriate in cases where only the handset’s orientation is needed but not the rate at which it is changed (accelerometer) or specific direction it’s pointing (compass). Determining Device Orientation You can use the SensorManager class to determine the orientation of the device. Although the Sensor.TYPE_ORIENTATION sensor value is deprecated, it is still valid on most popular devices. However, the newest recommended way is to use the getOrientation() method of the SensorManager class instead. Note We say newest way because the recommended method for determining device orientation has changed several times since Android was initially developed. shaking. How this is done depends on the purpose of the application. For instance, a simulated bubble level might need less smoothing than a game where too much sensitivity can be frustrating. 412 Chapter 19 Using Android’s Optional Hardware APIs The getOrientation() method takes two parameters: a rotation matrix and an array of three float values (azimuth [z], pitch [x], and roll [y]).