Android permite acceder a los sensores internos del dispositivo a través de las clases Sensor, SensorEvent y SensorManager, y de la interfaz SensorEventListener, del paquete android.hardware.

La clase Sensor acepta ocho tipos de sensores, como se puede ver en la referencia. Los sensores disponibles varían en función del aparato utilizado.

Listar los sensores del dispositivo

Para ver de qué sensores dispone nuestro dispositivo usamos la clase SensorManager:

   1: // Solicitamos al sistema el servicio que gestiona los sensores
   2: SensorManager mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
   3: // Peimos la lista con todos los sensores disponibles
   4: List<Sensor> listSensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
   5: // Iteramos y mostramos
   6: for(Sensor sensor:listSensors)
   7: {
   8:     Log.i("SENSOR", sensor.getName());
   9: }

La nueva versión de la SDK marca como obsoleto (deprecated) el método getSensors de la clase SensorManager, así como la interfaz SensorListener. Aunque los métodos antiguos son necesarios para versiones anteriores de la SDK, los ejemplos de este artículos se basan en la última versión.

Los sensores del HTC Magic

Esta es una lista de los valores devueltos por el código anterior ejecutándose en el HTC Magic:

  • AK8976A 3-axis Accelerometer
  • AK8976A 3-axis Magnetic field sensor
  • AK8976A Orientation sensor
  • AK8976A Temperature sensor

El AK8976A (arriba a la izquierda en esta figura, que muestra el hardware del HTC Dream) es una combinación de acelerómetro de tres ejes y magnetómetro de tres ejes. Combinando la lectura de los campos gravitatorio y magnético terrestres proporciona también información de orientación. Incluye además un sensor interno de temperatura, útil para comprobar si el móvil se está calentado demasiado.

Acceso a los datos del sensor

Para tener acceso a los datos del sensor debemos indicárselo al SensorManager con el método registerListener:

   1: public class MiActividad extends Activity {
   2:     MiVista mVista; // Clase que implemente SensorEventListener
   3:     // ...
   4:
   5:
   6:     @Override
   7:    protected void onCreate(Bundle savedInstanceState) {
   8:         super.onCreate(savedInstanceState);
   9:
  10:         // En esta clase recibiré los eventos y usaré el resultado para lo que quiera
  11:         mVista = new MiVista(this);
  12:         SensorManager mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
  13:
  14:         // Cada sensor se registra por separado
  15:         List<Sensor> listSensors = mSensorManager.getSensorList(Sensor.TYPE_MAGNETIC_FIELD);
  16:         Sensor orientationSensor = listSensors.get(0);
  17:         mSensorManager.registerListener(mTop, orientationSensor, SensorManager.SENSOR_DELAY_UI);
  18:
  19:         listSensors = mSensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
  20:         Sensor acelerometerSensor = listSensors.get(0);
  21:         mSensorManager.registerListener(mTop, acelerometerSensor, SensorManager.SENSOR_DELAY_UI);
  22:
  23:         listSensors = mSensorManager.getSensorList(Sensor.TYPE_MAGNETIC_FIELD);
  24:         Sensor magneticSensor = listSensors.get(0);
  25:         mSensorManager.registerListener(mTop, magneticSensor, SensorManager.SENSOR_DELAY_UI);
  26:
  27:         listSensors = mSensorManager.getSensorList(Sensor.TYPE_TEMPERATURE);
  28:         Sensor temperatureSensor = listSensors.get(0);
  29:         mSensorManager.registerListener(mTop, temperatureSensor, SensorManager.SENSOR_DELAY_UI);
  30:         ...
  31:     }
  32:     ...
  33: }
  34:
  35: class MiVista extends View implements SensorEventListener {
  36: ...
  37: }

Es necesario registrar cada tipo de sensor por separado para poder obtener información de todos ellos. El método registerListener toma como primer parámetro la instancia de la clase que implementa el SensorEventListener, y que veremos a continuación. El tercer parámetro acepta cuatro posibles valores, que indican al sistema con qué frecuencia nos gustaría recibir actualizaciones del sensor. Esta indicación sirve para que el sistema estime cuánta atención necesitan los sensores, pero no garantiza una frecuencia concreta.

Obtención de datos

Para recibir los datos tenemos que implementar dos métodos de SensorEventListener:

   1: class MiVista extends View implements SensorEventListener {
   2:     private float   mOrientationValues[] = new float[3];
   3:     private float   mAccelerometerValues[] = new float[3];
   4:     private float   mMagneticValues[] = new float[3];
   5:     private float   mTemperatureValues;
   6:
   7:     /*
   8:     * El resto del código de la clase para mostrar los datos
   9:     */
  10:
  11:     // En este ejemplo no necesitamos enterarnos de las variaciones de 
  12:     // precisión del sensor
  13:     @Override
  14:     public void onAccuracyChanged(Sensor sensor, int accuracy) {
  15:         // TODO Auto-generated method stub
  16:
  17:     }
  18:
  19:     @Override
  20:     public void onSensorChanged(SensorEvent event) {
  21:         // Cada sensor puede provocar que un thread pase por aquí, así 
  22:         // que sincronizamos el acceso
  23:         synchronized (this) {
  24:             switch(event.sensor.getType()) {
  25:             case Sensor.TYPE_ORIENTATION:
  26:                 for (int i=0 ; i<3 ; i++) {
  27:                     mOrientationValues[i] = event.values[i];
  28:                 }
  29:                 break;
  30:             case Sensor.TYPE_ACCELEROMETER:
  31:                 for (int i=0 ; i<3 ; i++) {
  32:                     mAccelerometerValues[i] = event.values[i];
  33:                 }
  34:                 break;
  35:             case Sensor.TYPE_MAGNETIC_FIELD:
  36:                 for (int i=0 ; i<3 ; i++) {
  37:                     mMagneticValues[i] = event.values[i];
  38:                 }
  39:                 break;
  40:             default:
  41:                 for (int i=0 ; i<event.values.length ; i++) {
  42:                     mTemperatureValues = event.values[i];
  43:                 }
  44:             }
  45:
  46:             invalidate();
  47:         }
  48:     }
  49: }

Cuando el evento se dispara en el método onSensorChanged comprobamos qué sensor lo ha causado y leemos los datos. Los posibles valores devueltos se indican en la documentación de la clase SensorEvent.

La clase SensorManager tiene además tres métodos (getInclination, getOrientation y getRotationMatrix), usados para calcular transformaciones de coordenadas. De ellos hablaremos en un próximo artículo.