Sensors (Accelerometer, Gyroscope)
Most phones carry a handful of physical sensors — an accelerometer, sometimes a gyroscope, a light sensor, a proximity sensor. Android exposes all of them through a single system service, the SensorManager, regardless of which sensor you actually want to read.
To keep things grounded, we'll add one small, playful feature to RecipeBox: shake the phone on the recipe list screen and get a random recipe suggestion — a "surprise me" gesture powered by the accelerometer.
Getting the SensorManager
// RecipeListActivity.java
private SensorManager sensorManager;
private Sensor accelerometer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recipe_list);
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (accelerometer == null) {
// Not every device has one — degrade gracefully
Log.w("RecipeList", "No accelerometer on this device, shake feature disabled");
}
}
Registering a SensorEventListener
A listener is registered in onResume(), so it's only active while the screen is visible:
private long lastShakeTime = 0;
private static final float SHAKE_THRESHOLD = 12f; // m/s^2 above gravity, tuned by feel
private final SensorEventListener shakeListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
double magnitude = Math.sqrt(x * x + y * y + z * z) - SensorManager.GRAVITY_EARTH;
long now = System.currentTimeMillis();
if (magnitude > SHAKE_THRESHOLD && (now - lastShakeTime) > 1000) {
lastShakeTime = now;
suggestRandomRecipe();
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Called when sensor accuracy changes; usually safe to ignore for a shake gesture
}
};
@Override
protected void onResume() {
super.onResume();
if (accelerometer != null) {
sensorManager.registerListener(shakeListener, accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}
}
- onSensorChanged() fires repeatedly with fresh x, y, z readings — dozens of times per second.
- Subtracting GRAVITY_EARTH isolates the extra acceleration caused by movement, not gravity itself sitting still.
- onAccuracyChanged() is required by the interface but for a simple shake gesture there's rarely anything useful to do in it.
- SENSOR_DELAY_NORMAL is fine here — we don't need the fastest possible sampling rate for a shake gesture.
Unregistering in onPause()
This is the one habit that genuinely matters with sensors: always unregister the listener when the screen is no longer visible.
@Override
protected void onPause() {
super.onPause();
sensorManager.unregisterListener(shakeListener);
}
Common Sensor Types
| Constant | Measures | Typical use |
|---|---|---|
| Sensor.TYPE_ACCELEROMETER | Acceleration on x/y/z axes (incl. gravity) | Shake detection, step counting, tilt |
| Sensor.TYPE_GYROSCOPE | Rotation rate around each axis | Orientation tracking, game controls |
| Sensor.TYPE_LIGHT | Ambient light level (lux) | Auto-adjusting screen/UI brightness |
| Sensor.TYPE_PROXIMITY | Distance to nearest object (often binary: near/far) | Turning off the screen during a phone call |
Checking Availability Before Use
Not every device has every sensor — a budget phone might skip the gyroscope, a tablet might have no proximity sensor at all. getDefaultSensor(type) is the standard availability check, and it always returns null rather than throwing when the sensor is missing:
Sensor gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
if (gyroscope != null) {
// safe to register a listener for it
} else {
// hide or disable whatever UI depends on it
}