Android Orientation Sensor tutorial

Android phones includes an orientation sensor that is used to detect the orientation of the phone in space. The orientation is given throught three values :

  1. Azimtuh in degres
    angle between the x axis of the phone and the north direction
    0 ≤ azimuth ≤ 360
  2. Pitch in degres
    angle made by the y axis of the phone relatively to the phone’s horizontal position
    -180 ≤ pitch ≤ 180
  3. Roll in degres
    angle made by the x axis of the phone relatively to the phone’s horizontal position
    -90 ≤ roll ≤ 90

In this tutorial, we’ll see how to use the orientation sensor in an application in order to capture orientation change events. We’ll use these events to build a custom listener that will trigger phone’s orientation change events :

package net.androgames.blog.sample.orientation;
 
public interface OrientationListener {
 
    public void onOrientationChanged(float azimuth, 
            float pitch, float roll);
 
    /**
     * Top side of the phone is up
     * The phone is standing on its bottom side
     */
    public void onTopUp();
 
    /**
     * Bottom side of the phone is up
     * The phone is standing on its top side
     */
    public void onBottomUp();
 
    /**
     * Right side of the phone is up
     * The phone is standing on its left side
     */
    public void onRightUp();
 
    /**
     * Left side of the phone is up
     * The phone is standing on its right side
     */
    public void onLeftUp();
 
}

An instance of the SensorManager is needed in order to retrieve informations about the supported sensors. No permission is required to access the sensor service and to retrieve the list of available orientation sensors given by the Sensor.TYPE_ORIENTATION constant. If at least one Sensor exists, it is possible to register a SensorEventListener for a Sensor of the list. The delivering rate for the orientation sensor events can be specified and must be one of these :

  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

The custom orientation manager is as follow :

package net.androgames.blog.sample.orientation;
 
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 Orientation Sensor Manager Archetype
 * @author antoine vianey
 * under GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
 */
public class OrientationManager {
 
    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 OrientationListener listener;
 
    /** indicates whether or not Orientation Sensor is supported */
    private static Boolean supported;
    /** indicates whether or not Orientation Sensor is running */
    private static boolean running = false;
 
    /** Sides of the phone */
    enum Side {
        TOP,
        BOTTOM,
        LEFT,
        RIGHT;
    }
 
    /**
     * 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 Orientation sensor is available
     */
    public static boolean isSupported() {
        if (supported == null) {
            if (Orientation.getContext() != null) {
                sensorManager = (SensorManager) Orientation.getContext()
                        .getSystemService(Context.SENSOR_SERVICE);
                List<Sensor> sensors = sensorManager.getSensorList(
                        Sensor.TYPE_ORIENTATION);
                supported = new Boolean(sensors.size() > 0);
            } else {
                supported = Boolean.FALSE;
            }
        }
        return supported;
    }
 
    /**
     * Registers a listener and start listening
     */
    public static void startListening(
            OrientationListener orientationListener) {
        sensorManager = (SensorManager) Orientation.getContext()
                .getSystemService(Context.SENSOR_SERVICE);
        List<Sensor> sensors = sensorManager.getSensorList(
                Sensor.TYPE_ORIENTATION);
        if (sensors.size() > 0) {
            sensor = sensors.get(0);
            running = sensorManager.registerListener(
                    sensorEventListener, sensor, 
                    SensorManager.SENSOR_DELAY_NORMAL);
            listener = orientationListener;
        }
    }
 
    /**
     * The listener that listen to events from the orientation listener
     */
    private static SensorEventListener sensorEventListener = 
        new SensorEventListener() {
 
        /** The side that is currently up */
        private Side currentSide = null;
        private Side oldSide = null;
        private float azimuth;
        private float pitch;
        private float roll;
 
        public void onAccuracyChanged(Sensor sensor, int accuracy) {}
 
        public void onSensorChanged(SensorEvent event) {
 
            azimuth = event.values[0];     // azimuth
            pitch = event.values[1];     // pitch
            roll = event.values[2];        // roll
 
            if (pitch < -45 && pitch > -135) {
                // top side up
                currentSide = Side.TOP;
            } else if (pitch > 45 && pitch < 135) {
                // bottom side up
                currentSide = Side.BOTTOM;
            } else if (roll > 45) {
                // right side up
                currentSide = Side.RIGHT;
            } else if (roll < -45) {
                // left side up
                currentSide = Side.LEFT;
            }
 
            if (currentSide != null && !currentSide.equals(oldSide)) {
                switch (currentSide) {
                    case TOP : 
                        listener.onTopUp();
                        break;
                    case BOTTOM : 
                        listener.onBottomUp();
                        break;
                    case LEFT: 
                        listener.onLeftUp();
                        break;
                    case RIGHT: 
                        listener.onRightUp();
                        break;
                }
                oldSide = currentSide;
            }
 
            // forwards orientation to the OrientationListener
            listener.onOrientationChanged(azimuth, pitch, roll);
        }
 
    };
 
}

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

package net.androgames.blog.sample.orientation;
 
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
 
/**
 * Android orientation sensor tutorial
 * @author antoine vianey
 * under GPL v3 : http://www.gnu.org/licenses/gpl-3.0.html
 */
public class Orientation extends Activity implements OrientationListener {
 
    private static Context CONTEXT;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        CONTEXT = this;
    }
 
    protected void onResume() {
        super.onResume();
        if (OrientationManager.isSupported()) {
            OrientationManager.startListening(this);
        }
    }
 
    protected void onDestroy() {
        super.onDestroy();
        if (OrientationManager.isListening()) {
            OrientationManager.stopListening();
        }
 
    }
 
    public static Context getContext() {
        return CONTEXT;
    }
 
    @Override
    public void onOrientationChanged(float azimuth, 
            float pitch, float roll) {
        ((TextView) findViewById(R.id.azimuth)).setText(
                String.valueOf(azimuth));
        ((TextView) findViewById(R.id.pitch)).setText(
                String.valueOf(pitch));
        ((TextView) findViewById(R.id.roll)).setText(
                String.valueOf(roll));
    }
 
    @Override
    public void onBottomUp() {
        Toast.makeText(this, "Bottom UP", 1000).show();
    }
 
    @Override
    public void onLeftUp() {
        Toast.makeText(this, "Left UP", 1000).show();
    }
 
    @Override
    public void onRightUp() {
        Toast.makeText(this, "Right UP", 1000).show();
    }
 
    @Override
    public void onTopUp() {
        Toast.makeText(this, "Top UP", 1000).show();
    }
 
}

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 : SampleOrientationSensor

Enjoy !

Tags : ,

One Response to “Android Orientation Sensor tutorial”

  1. Gary Says:
    March 17th, 2010 at 4:21 am

    At last, a well written, easy to understand tutorial.

    Added to that it actually works.

    Thank you.

Leave a Reply