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:
- Create a new class named ShakeDetector
- Define variables for sensor manager, shake detector and accelerometer
- Add/register the sensor manager and listener in the onCreate() method in your activity
- Configure onResume and onPause to activate/deactivate the accelerometer
- 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:
Leave a Reply
You must be logged in to post a comment.