Android Accelerometer Sensor tutorial

Android supports a wide variety of sensors that can be used by applications to capture informations about the phone’s environment.

In this tutorial, we’ll see how to use the accelerometer sensor in an application in order to capture acceleration change events. We’ll use these events to build a custom listener that will trigger others events such as shake events :

package net.androgames.bolg.sample.accelerometer;
 
public interface AccelerometerListener {
 
	public void onAccelerationChanged(float x, float y, float z);
 
	public void onShake(float force);
 
}

An instance of the SensorManager is required in order to retrieve informations about the supported sensors. No permission is required to access the sensor service. It is then possible to retrieve the list of available sensors of a certain type. For an accelerometer sensor, the type to use is given by the Sensor.TYPE_ACCELEROMETER constant. If at least one Sensor exists, it is possible to register a SensorEventListener for a Sensor of the list. It is possible to specify the delivering rate for sensor events. Specified rate must be one of :

  1. SensorManager.SENSOR_DELAY_FASTEST : as fast as possible
  2. SensorManager.SENSOR_DELAY_GAME : rate suitable for game
  3. SensorManager.SENSOR_DELAY_NORMAL : normal rate
  4. SensorManager.SENSOR_DELAY_UI : rate suitable for UI Thread

In order to allow a correct shake detection, it is preferable to use at least SensorManager.SENSOR_DELAY_GAME.

The custom accelerometer manager is as follow :

package net.androgames.bolg.sample.accelerometer;
 
import java.util.List;
 
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
 
/**
 * Android Accelerometer Sensor Manager Archetype
 * @author antoine vianey
 * under GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
 */
public class AccelerometerManager {
 
    /** Accuracy configuration */
    private static float threshold     = 0.2f;
    private static int interval     = 1000;
 
    private static Sensor sensor;
    private static SensorManager sensorManager;
    // you could use an OrientationListener array instead
    // if you plans to use more than one listener
    private static AccelerometerListener listener;
 
    /** indicates whether or not Accelerometer Sensor is supported */
    private static Boolean supported;
    /** indicates whether or not Accelerometer Sensor is running */
    private static boolean running = false;
 
    /**
     * Returns true if the manager is listening to orientation changes
     */
    public static boolean isListening() {
        return running;
    }
 
    /**
     * Unregisters listeners
     */
    public static void stopListening() {
        running = false;
        try {
            if (sensorManager != null && sensorEventListener != null) {
                sensorManager.unregisterListener(sensorEventListener);
            }
        } catch (Exception e) {}
    }
 
    /**
     * Returns true if at least one Accelerometer sensor is available
     */
    public static boolean isSupported() {
        if (supported == null) {
            if (Accelerometer.getContext() != null) {
                sensorManager = (SensorManager) Accelerometer.getContext().
                        getSystemService(Context.SENSOR_SERVICE);
                List<Sensor> sensors = sensorManager.getSensorList(
                        Sensor.TYPE_ACCELEROMETER);
                supported = new Boolean(sensors.size() > 0);
            } else {
                supported = Boolean.FALSE;
            }
        }
        return supported;
    }
 
    /**
     * Configure the listener for shaking
     * @param threshold
     *             minimum acceleration variation for considering shaking
     * @param interval
     *             minimum interval between to shake events
     */
    public static void configure(int threshold, int interval) {
        AccelerometerManager.threshold = threshold;
        AccelerometerManager.interval = interval;
    }
 
    /**
     * Registers a listener and start listening
     * @param accelerometerListener
     *             callback for accelerometer events
     */
    public static void startListening(
            AccelerometerListener accelerometerListener) {
        sensorManager = (SensorManager) Accelerometer.getContext().
                getSystemService(Context.SENSOR_SERVICE);
        List<Sensor> sensors = sensorManager.getSensorList(
                Sensor.TYPE_ACCELEROMETER);
        if (sensors.size() > 0) {
            sensor = sensors.get(0);
            running = sensorManager.registerListener(
                    sensorEventListener, sensor, 
                    SensorManager.SENSOR_DELAY_GAME);
            listener = accelerometerListener;
        }
    }
 
    /**
     * Configures threshold and interval
     * And registers a listener and start listening
     * @param accelerometerListener
     *             callback for accelerometer events
     * @param threshold
     *             minimum acceleration variation for considering shaking
     * @param interval
     *             minimum interval between to shake events
     */
    public static void startListening(
            AccelerometerListener accelerometerListener, 
            int threshold, int interval) {
        configure(threshold, interval);
        startListening(accelerometerListener);
    }
 
    /**
     * The listener that listen to events from the accelerometer listener
     */
    private static SensorEventListener sensorEventListener = 
        new SensorEventListener() {
 
        private long now = 0;
        private long timeDiff = 0;
        private long lastUpdate = 0;
        private long lastShake = 0;
 
        private float x = 0;
        private float y = 0;
        private float z = 0;
        private float lastX = 0;
        private float lastY = 0;
        private float lastZ = 0;
        private float force = 0;
 
        public void onAccuracyChanged(Sensor sensor, int accuracy) {}
 
        public void onSensorChanged(SensorEvent event) {
            // use the event timestamp as reference
            // so the manager precision won't depends 
            // on the AccelerometerListener implementation
            // processing time
            now = event.timestamp;
 
            x = event.values[0];
            y = event.values[1];
            z = event.values[2];
 
            // if not interesting in shake events
            // just remove the whole if then else bloc
            if (lastUpdate == 0) {
                lastUpdate = now;
                lastShake = now;
                lastX = x;
                lastY = y;
                lastZ = z;
            } else {
                timeDiff = now - lastUpdate;
                if (timeDiff > 0) {
                    force = Math.abs(x + y + z - lastX - lastY - lastZ) 
                                / timeDiff;
                    if (force > threshold) {
                        if (now - lastShake >= interval) {
                            // trigger shake event
                            listener.onShake(force);
                        }
                        lastShake = now;
                    }
                    lastX = x;
                    lastY = y;
                    lastZ = z;
                    lastUpdate = now;
                }
            }
            // trigger change event
            listener.onAccelerationChanged(x, y, z);
        }
 
    };
 
}

In the case of a SensorEvent triggered by a Sensor of type Sensor.TYPE_ACCELEROMETER, the event’s values represents the acceleration of the phone given by a vector in a cartesian coordinate system. Landing on a table, the values returned by the SensorEvent for the phone should be :

  1. 0 m/s2 along x axis
  2. 0 m/s2 along y axis
  3. 9,80665 m/s2 along z axis

From an event to another, the coordinates of the acceleration vector are stored to detect suddent acceleration changes and to trigger a shake event when the threshold is reached. Others events could be implemented such as the detection of up and down gestures, circular gestures and lot more…

The custom AccelerometerManager can be use in any Activity or Service :

package net.androgames.bolg.sample.accelerometer;
 
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
 
/**
 * Android accelerometer sensor tutorial
 * @author antoine vianey
 * under GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
 */
public class Accelerometer extends Activity 
        implements AccelerometerListener {
 
    private static Context CONTEXT;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        CONTEXT = this;
    }
 
    protected void onResume() {
        super.onResume();
        if (AccelerometerManager.isSupported()) {
            AccelerometerManager.startListening(this);
        }
    }
 
    protected void onDestroy() {
        super.onDestroy();
        if (AccelerometerManager.isListening()) {
            AccelerometerManager.stopListening();
        }
 
    }
 
    public static Context getContext() {
        return CONTEXT;
    }
 
    /**
     * onShake callback
     */
    public void onShake(float force) {
        Toast.makeText(this, "Phone shaked : " + force, 1000).show();
    }
 
    /**
     * onAccelerationChanged callback
     */
    public void onAccelerationChanged(float x, float y, float z) {
        ((TextView) findViewById(R.id.x)).setText(String.valueOf(x));
        ((TextView) findViewById(R.id.y)).setText(String.valueOf(y));
        ((TextView) findViewById(R.id.z)).setText(String.valueOf(z));
    }
 
}

As usual, it is preferable to register listeners in the onResume() method of the Activity and removed in the onFinish() method of the Activity.

You can browse the full source code of the Eclipse project here : SampleAccelerometerSensor

Enjoy !

Tags: , , ,