I haven't posted in a while, so I thought I'd post a little bit of code that I wrote for an android phone app that I'm working on. You see, Google recently deprecated one of the older ways of getting tilt data from the Android OS, but they didn't really document the new way of doing it. Plus, most resources I found online still use the old method, so I wrote a class to get the tilt data in an easy-to-use manner. (And without using any deprecated functions.)
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
public class TiltCalc {
private boolean needsRecalc = false;
private float[] tilt_data = {0, 0, 0}, gravity = {0, 0, 0}, magnet = {0, 0, 0};
// Change this to make the sensors respond quicker, or slower:
private static final int delay = SensorManager.SENSOR_DELAY_GAME;
// Special class used to handle sensor events:
private final SensorEventListener listen = new SensorEventListener() {
public void onSensorChanged(SensorEvent e) {
final float[] vals = e.values, target;
// Just capture the Gyroscope data, if it exists:
//if(e.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
// System.arraycopy(vals, 0, tilt_data, 0, 3);
// return;
//}
// Else, we'll capture the data, and mark the class for a re-calc:
target = (e.sensor.getType() == Sensor.TYPE_ACCELEROMETER) ? gravity : magnet;
needsRecalc = true;
System.arraycopy(vals, 0, target, 0, 3);
}
public void onAccuracyChanged(Sensor event, int res) {}
};
// The constructor will use a context object to register itself for various inputs:
public TiltCalc(Context c) {
SensorManager man = (SensorManager) c.getSystemService(Context.SENSOR_SERVICE);
Sensor mag_sensor = man.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
Sensor acc_sensor = man.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
//Sensor gyr_sensor = man.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
// Turns out Android's gyroscope doesn't work this way, so it's been disabled for now...
//if(man.registerListener(listen, gyr_sensor, delay)) {
// Log.d("TiltCalc", "Gyroscope detected, and successfully connected.");
// Use an accelerometer+compass approach:
//} else
if( man.registerListener(listen, mag_sensor, delay) &&
man.registerListener(listen, acc_sensor, delay) ) {
Log.d("TiltCalc", "No gyroscope, falling back on accelerometer+compass.");
} else {
Log.d("TiltCalc", "No acceptable hardware found.");
// We will remove the listener, just in case one of the accelerometer sensors
// registered, just not the other one:
man.unregisterListener(listen);
}
}
// Will return the most up-to-date tilt data in the vals[] array
public void getTilt(float[] vals) {
// If some of the data has been changed, then we need to recalculate some things...
if(needsRecalc) {
float[] R={0,0,0,0,0,0,0,0,0};
// Calculate the rotation matrix, and use that to get the orientation:
if(SensorManager.getRotationMatrix(R, null, gravity, magnet))
SensorManager.getOrientation(R, tilt_data);
needsRecalc = false;
}
System.arraycopy(tilt_data, 0, vals, 0, 3);
}
}
The constructor needs a context object, so that it can register itself for sensor updates. The easiest way to do this is to call the function in the main activity, using 'this' as the context. (Activity is a subclass of Context...)
Then, you would just call getTilt(float[] vals) to get the 3 tilt values. That function will return the 3 tilt values into the float[] that is passed into the class. The tilt values will be setup like:

So vals[0] is rotating the phone around like a compass, vals[1] is tilting the phone up and down, and vals[2] is tilting the phone left and right.
This class is coded so that if you have an accurate gyroscope in the phone, the class will use that to get the tilt data. However, in most cases, you won't have a gyroscope, so the class will fall back on a less accurate (but still perfectly usable) accelerometer + compass method of calculating tilt.
Note: I do not have a phone with a gyroscope, so that code is ENTIRELY untested. Plus, since Google didn't document the output format of gyroscope updates, this code is based on the very bold assumption that Google wouldn't give tilt data in differing formats based on the data's source. (EDIT: Turns out this *WAS* a very bold assumption... :P)
Anyway, this code is public domain, so have fun. :)
EDIT (19 Jan 2011): Now that there are Android phones with gyroscopes, we know that they don't work in the way I was expecting, so the gyroscope code has been commented out. I'll update the code again once I know for sure how they work...