So I’ve been playing around with Android and have a little app that I wanted the user to be able to shake the phone and have something happen. I did some digging and the following is a tutorial on how to setup a shake listener to capture a shake and then do whatever you want.

This is by no means something that I’ve created, I just used examples that I found on stackoverflow.com. Also, this uses G-force to calculate the shake threshold. Many thanks to Peterdk and Akos Cz for their input and answer for this solution. Peterdk recommended using the G-Force app by Blake La Pierre on Google Play Store if you want to get the actual G-force value on your phone and tweak it in the code below.

Long story short, there are 5 things that need to happen to get this to work:

  1. Create a new class named ShakeDetector
  2. Define variables for sensor manager, shake detector and accelerometer
  3. Add/register the sensor manager and listener in the onCreate() method in your activity
  4. Configure onResume and onPause to activate/deactivate the accelerometer
  5. Edit the AndroidManifest.xml file to require the device to have an accelerometer

Here’s how to do it all:

Step 1

Create a new class named ShakeDetector and put the following code in it:

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.FloatMath;

public class ShakeDetector implements SensorEventListener {

	/*
	 * The gForce that is necessary to register as shake.
	 * Must be greater than 1G (one earth gravity unit).
	 * You can install "G-Force", by Blake La Pierre
	 * from the Google Play Store and run it to see how
	 *  many G's it takes to register a shake
	 */
	private static final float SHAKE_THRESHOLD_GRAVITY = 2.7F;
	private static final int SHAKE_SLOP_TIME_MS = 500;
	private static final int SHAKE_COUNT_RESET_TIME_MS = 3000;

	private OnShakeListener mListener;
	private long mShakeTimestamp;
	private int mShakeCount;

	public void setOnShakeListener(OnShakeListener listener) {
		this.mListener = listener;
	}

	public interface OnShakeListener {
		public void onShake(int count);
	}

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		// ignore
	}

	@Override
	public void onSensorChanged(SensorEvent event) {

		if (mListener != null) {
			float x = event.values[0];
			float y = event.values[1];
			float z = event.values[2];

			float gX = x / SensorManager.GRAVITY_EARTH;
			float gY = y / SensorManager.GRAVITY_EARTH;
			float gZ = z / SensorManager.GRAVITY_EARTH;

			// gForce will be close to 1 when there is no movement.
			float gForce = FloatMath.sqrt(gX * gX + gY * gY + gZ * gZ);

			if (gForce > SHAKE_THRESHOLD_GRAVITY) {
				final long now = System.currentTimeMillis();
				// ignore shake events too close to each other (500ms)
				if (mShakeTimestamp + SHAKE_SLOP_TIME_MS > now) {
					return;
				}

				// reset the shake count after 3 seconds of no shakes
				if (mShakeTimestamp + SHAKE_COUNT_RESET_TIME_MS < now) {
					mShakeCount = 0;
				}

				mShakeTimestamp = now;
				mShakeCount++;

				mListener.onShake(mShakeCount);
			}
		}
	}
}

I get a warning in Eclipse about the sqrt on line 50 of the code above saying “Use java.lang.Math#sqrt instead of android.util.FloatMath#sqrt() since it is faster as of API 8”, but I choose to ignore it.

Step 2

At the beginning of your activity, declare the following variables:

	// The following are used for the shake detection
	private SensorManager mSensorManager;
	private Sensor mAccelerometer;
	private ShakeDetector mShakeDetector;

Step 3

Now, in your activity, paste the following in the onCreate() method:

		// ShakeDetector initialization
		mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
		mAccelerometer = mSensorManager
				.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		mShakeDetector = new ShakeDetector();
		mShakeDetector.setOnShakeListener(new OnShakeListener() {

			@Override
			public void onShake(int count) {
				/*
				 * The following method, "handleShakeEvent(count):" is a stub //
				 * method you would use to setup whatever you want done once the
				 * device has been shook.
				 */
				handleShakeEvent(count);
			}
		});

Take care of any missing import statements (ctrl+shift+o in Eclipse). Create your own handleShakeEvent method to do what you want when a shake is detected. Or, remove the stub reference to handleShakeEvent(count); in the code above and create your own method or add what you want in its place.

Step 4

Modify (or add) onResume and onPause to include the following line:

	@Override
	public void onResume() {
		super.onResume();
		// Add the following line to register the Session Manager Listener onResume
		mSensorManager.registerListener(mShakeDetector, mAccelerometer,	SensorManager.SENSOR_DELAY_UI);
	}

	@Override
	public void onPause() {
		// Add the following line to unregister the Sensor Manager onPause
		mSensorManager.unregisterListener(mShakeDetector);
		super.onPause();
	}

Step 5

I also added the following in my AndroidManifest.xml to make sure the phone has an accelerometer (it goes right after the <uses-sdk /> section):

<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true" />

There you have it. I hope this helps!

Sources: