257

I need to add a shake feature that will refresh my Android application.

All I find of documentation involves implementing the SensorListener, but Eclipse tells me it's deprecated and suggest SensorEventListener.

Anybody that has a nice guide to how I go about creating this shake controller?

itsmysterybox
  • 2,218
  • 3
  • 18
  • 25
Sara
  • 3,723
  • 6
  • 23
  • 29
  • Found a example that worked at last: http://android.hlidskialf.com/blog/code/android-shake-detection-listener – Sara Feb 23 '10 at 15:27
  • Since the solution at the URL given by Sara uses a deprecated class, I slightly modified it [here](http://www.sigwann.com/blog/2011/03/android-shake-detection-accelerometer/) to make it works – Sigwann Mar 12 '11 at 15:58
  • 17
    This is old but just came across it and had to +1 for the title – codeMagic Jul 29 '13 at 02:15

16 Answers16

318

Here is an example code. Put this into your activity class:

  /* put this into your activity class */
  private SensorManager mSensorManager;
  private float mAccel; // acceleration apart from gravity
  private float mAccelCurrent; // current acceleration including gravity
  private float mAccelLast; // last acceleration including gravity

  private final SensorEventListener mSensorListener = new SensorEventListener() {

    public void onSensorChanged(SensorEvent se) {
      float x = se.values[0];
      float y = se.values[1];
      float z = se.values[2];
      mAccelLast = mAccelCurrent;
      mAccelCurrent = (float) Math.sqrt((double) (x*x + y*y + z*z));
      float delta = mAccelCurrent - mAccelLast;
      mAccel = mAccel * 0.9f + delta; // perform low-cut filter
    }

    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
  };

  @Override
  protected void onResume() {
    super.onResume();
    mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
  }

  @Override
  protected void onPause() {
    mSensorManager.unregisterListener(mSensorListener);
    super.onPause();
  }

And add this to your onCreate method:

    /* do this in onCreate */
    mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
    mAccel = 0.00f;
    mAccelCurrent = SensorManager.GRAVITY_EARTH;
    mAccelLast = SensorManager.GRAVITY_EARTH;

You can then ask "mAccel" wherever you want in your application for the current acceleration, independent from the axis and cleaned from static acceleration such as gravity. It will be approx. 0 if there is no movement, and, lets say >2 if the device is shaked.

Based on the comments - to test this:

if (mAccel > 12) {
    Toast toast = Toast.makeText(getApplicationContext(), "Device has shaken.", Toast.LENGTH_LONG);
    toast.show();
}

Notes:

The accelometer should be deactivated onPause and activated onResume to save resources (CPU, Battery). The code assumes we are on planet Earth ;-) and initializes the acceleration to earth gravity. Otherwise you would get a strong "shake" when the application starts and "hits" the ground from free-fall. However, the code gets used to the gravitation due to the low-cut filter and would work also on other planets or in free space, once it is initialized. (you never know how long your application will be in use...;-)

Mahendra Liya
  • 11,166
  • 10
  • 79
  • 105
Thilo Köhler
  • 3,591
  • 2
  • 15
  • 10
  • Is this code going to handle the case where the phone doesn't have an accelerometer? I don't have a device without one and the emulator doesn't work so I was hoping somebody might be in the know about this subject. – jmurphy Mar 09 '11 at 05:03
  • 10
    Is this line of code `mAccel = mAccel * 0.9f + delta; // perform low-cut filter` supposed to be `mAccel = mAccel * 0.9f + delta * 0.1f; // perform low-cut filter`? – Randy Sugianto 'Yuku' Mar 19 '12 at 09:38
  • 3
    Nice one! I've also added a check to avoid shakes too often (in my app I've set it to 750 ms after the last shake)... `Calendar last = Calendar.getInstance(); Calendar now = Calendar.getInstance(); last.setTime(last_date); now.setTime(new Date()); long diff = now.getTimeInMillis() - last.getTimeInMillis(); if(diff >= 750) { ... } ` Method names should be correct (I don't have the code with me...) – TesX Mar 29 '12 at 10:46
  • 9
    The compiler suggests to use: `android.util.FloatMath.sqrt(x*x + y*y + z*z);` instead to avoid a conversion – Casebash Sep 20 '12 at 14:45
  • The "match" for `onResume()` is `onPause()`, not `onStop()`. I edited your answer to correct this. – Marcin Orlowski Dec 13 '12 at 11:43
  • 3
    To test this, use the following: if (mAccel > 12) { Toast toast = Toast.makeText(getApplicationContext(), "Device has shaken.", Toast.LENGTH_LONG); toast.show(); } –  Aug 21 '13 at 19:09
  • I've found (on a galaxy s4) that mAccel >12 is a normal wrist flick, don't know about other devices. Be careful about the sensitivity. –  Aug 21 '13 at 19:10
  • 1
    Where do I check the value of mAccel? In my onCreate or inside of OnSernsorChanged? – Vanquiza May 24 '14 at 23:34
  • 1
    This will detect any sudden SINGLE movement which may not be a shake - just dropping the phone will activate it. – CloudWalker Jul 09 '14 at 09:01
  • @Vanquiza put the mAccel check in OnSensorChanged method. – Marka A Sep 07 '14 at 00:14
  • is there a way to calculate the max a smartphone can have for mAccel ? – Tsunaze Oct 14 '14 at 13:41
  • How can i verify this shaking pattern ? How can i store in SQLite database for future verification purpose? – sam_k Jun 01 '15 at 16:04
  • How to do it but detecting the vertical move? I mean from top to bottom (Like smashing) – Skizo-ozᴉʞS Jan 25 '16 at 15:13
  • Would this detect three shake? I want to play an audio on three shake. – Si8 Apr 11 '16 at 20:23
  • @CloudWalker can you please tell me how do i get toast only when mobile fall down not when i shake it. – Abhishek Apr 13 '16 at 05:16
  • @Abhishek - Probably by setting lower limit for acceleration although you can always shake it very strong but you can also filter by duration and lack of any acceleration when it's on the floor - short spike of strong acceleration followed by no movement for couple of seconds (until owner picks it up again). You plan to sound alarm if the phone drops so it won't get unnoticed? Nice idea! – CloudWalker Apr 13 '16 at 11:04
  • @CloudWalker please have a look on this http://stackoverflow.com/questions/36540058/can-anyone-tell-me-how-i-get-toast-when-mobile-falls-down – Abhishek Apr 13 '16 at 11:14
  • Are you sure you want the difference of the magnitudes, rather than the magnitude of the difference? The way you have it (difference of magnitudes), it will only notice up-down shake (i.e. in direction of current gravity); it doesn't notice sideways shake. This was pretty mystifying to me until I realized what was going on. – Don Hatch Apr 07 '18 at 03:13
130

Here is my code for shake gesture detection:

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


/**
 * Listener that detects shake gesture.
 */
public class ShakeEventListener implements SensorEventListener {


  /** Minimum movement force to consider. */
  private static final int MIN_FORCE = 10;

  /**
   * Minimum times in a shake gesture that the direction of movement needs to
   * change.
   */
  private static final int MIN_DIRECTION_CHANGE = 3;

  /** Maximum pause between movements. */
  private static final int MAX_PAUSE_BETHWEEN_DIRECTION_CHANGE = 200;

  /** Maximum allowed time for shake gesture. */
  private static final int MAX_TOTAL_DURATION_OF_SHAKE = 400;

  /** Time when the gesture started. */
  private long mFirstDirectionChangeTime = 0;

  /** Time when the last movement started. */
  private long mLastDirectionChangeTime;

  /** How many movements are considered so far. */
  private int mDirectionChangeCount = 0;

  /** The last x position. */
  private float lastX = 0;

  /** The last y position. */
  private float lastY = 0;

  /** The last z position. */
  private float lastZ = 0;

  /** OnShakeListener that is called when shake is detected. */
  private OnShakeListener mShakeListener;

  /**
   * Interface for shake gesture.
   */
  public interface OnShakeListener {

    /**
     * Called when shake gesture is detected.
     */
    void onShake();
  }

  public void setOnShakeListener(OnShakeListener listener) {
    mShakeListener = listener;
  }

  @Override
  public void onSensorChanged(SensorEvent se) {
    // get sensor data
    float x = se.values[SensorManager.DATA_X];
    float y = se.values[SensorManager.DATA_Y];
    float z = se.values[SensorManager.DATA_Z];

    // calculate movement
    float totalMovement = Math.abs(x + y + z - lastX - lastY - lastZ);

    if (totalMovement > MIN_FORCE) {

      // get time
      long now = System.currentTimeMillis();

      // store first movement time
      if (mFirstDirectionChangeTime == 0) {
        mFirstDirectionChangeTime = now;
        mLastDirectionChangeTime = now;
      }

      // check if the last movement was not long ago
      long lastChangeWasAgo = now - mLastDirectionChangeTime;
      if (lastChangeWasAgo < MAX_PAUSE_BETHWEEN_DIRECTION_CHANGE) {

        // store movement data
        mLastDirectionChangeTime = now;
        mDirectionChangeCount++;

        // store last sensor data 
        lastX = x;
        lastY = y;
        lastZ = z;

        // check how many movements are so far
        if (mDirectionChangeCount >= MIN_DIRECTION_CHANGE) {

          // check total duration
          long totalDuration = now - mFirstDirectionChangeTime;
          if (totalDuration < MAX_TOTAL_DURATION_OF_SHAKE) {
            mShakeListener.onShake();
            resetShakeParameters();
          }
        }

      } else {
        resetShakeParameters();
      }
    }
  }

  /**
   * Resets the shake parameters to their default values.
   */
  private void resetShakeParameters() {
    mFirstDirectionChangeTime = 0;
    mDirectionChangeCount = 0;
    mLastDirectionChangeTime = 0;
    lastX = 0;
    lastY = 0;
    lastZ = 0;
  }

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

}

Add this in your activity:

  private SensorManager mSensorManager;

  private ShakeEventListener mSensorListener;

...

in onCreate() add:

    mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    mSensorListener = new ShakeEventListener();   

    mSensorListener.setOnShakeListener(new ShakeEventListener.OnShakeListener() {

      public void onShake() {
        Toast.makeText(KPBActivityImpl.this, "Shake!", Toast.LENGTH_SHORT).show();
      }
    });

and:

@Override
  protected void onResume() {
    super.onResume();
    mSensorManager.registerListener(mSensorListener,
        mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
        SensorManager.SENSOR_DELAY_UI);
  }

  @Override
  protected void onPause() {
    mSensorManager.unregisterListener(mSensorListener);
    super.onPause();
  }
peceps
  • 16,533
  • 10
  • 70
  • 77
  • 2
    Why do you register the listener twice, once in onCreate and again in onResume? onCreate is never called without onResume, so you can remove the register call in onCreate. – Matthias Apr 19 '12 at 20:53
  • 1
    Also, if registered in onResume, you'll probably want to unregister in onPause, not onStop. Apart from that, great stuff, much appreciated. – Matthias Apr 19 '12 at 20:54
  • It works great for me. Than you peceps, great work. @Matthias, what do you mean by that? Is it possible to share your working code? – Droid-Bird Apr 20 '12 at 13:49
  • 1
    The listener should be unregistered in onPause(), not onStop(). If you leave your phone on this Activity it will "eat" all your battery. – lomza May 14 '12 at 10:33
  • 2
    @lomza you are probably right, it should be unregistered onPause(), now changed. – peceps May 17 '12 at 14:16
  • 1
    Don't forget `mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);` in onCreate(). Otherwise, works wonderfully! Thanks! – BVB Jun 01 '12 at 02:39
  • Thanks for your code.. I am so confused how to identify when the shake get stopped.. Could you suggest some idea please – vinothp Aug 01 '12 at 14:18
  • Where should your first code snippet (_ShakeEventListener_ class) be placed? In a file called `ShakeEventListener.java`? That's how I currently have it but I'm getting two errors. "_The method onAccuracyChanged(Sensor, int) of type ShakeEventListener must override a superclass method_" And the same error but for `onSensorChanged(SensorEvent)`. – Dennis Oct 16 '12 at 20:05
  • @Dennis remove the '@Override' from the methods. – peceps Oct 18 '12 at 14:58
  • Hi peceps, Does this method works in all the android devices ?? Actually I am getting force close in some devices, when the user shakes the device for long time, repeteadly. Can you please help me in this ?? – Gaurav Arora Aug 22 '13 at 04:11
  • Your `onPause()` should call `super.onPause()`, and not `super.onStop()` – ToothlessRebel Oct 27 '13 at 20:12
  • @peceps How can i verify this shaking pattern ? How can i store in SQLite database for future verification purpose? – sam_k Jun 01 '15 at 16:04
  • This is a copy from this project https://github.com/satyan/funnyanimal/blob/master/src/com/satya/funnyanimal/ShakeEventListener.java – Jorgesys Jul 30 '15 at 22:05
  • I would like to detect three shakes... Can it do that? Thanks – Si8 Apr 11 '16 at 20:24
  • `DATA_X` `DATA_Y` and `DATA_Z` are all deprecated. – Si8 Apr 13 '16 at 01:37
  • @peceps can you tell how it detect only when mobile falls down, i mean i want to send a sms to emergency contact when mobile falls down not on shake event – Abhishek Apr 13 '16 at 04:48
  • `float x = se.values[SensorManager.DATA_X]` is deprecated. Instead use `float x = se.values[0]` and respectively `1` and `2` for `y` and `z`. – Mr. Nobody Jul 13 '16 at 13:31
33

Here's yet another implementation that builds on some of the tips in here as well as the code from the Android developer site.

MainActivity.java

public class MainActivity extends Activity {

    private ShakeDetector mShakeDetector;
    private SensorManager mSensorManager;
    private Sensor mAccelerometer;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // ShakeDetector initialization
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mShakeDetector = new ShakeDetector(new OnShakeListener() {
            @Override
            public void onShake() {
                // Do stuff!
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        mSensorManager.registerListener(mShakeDetector, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
    }

    @Override
    protected void onPause() {
        mSensorManager.unregisterListener(mShakeDetector);
        super.onPause();
    }   
}

ShakeDetector.java

package com.example.test;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;

public class ShakeDetector implements SensorEventListener {

    // Minimum acceleration needed to count as a shake movement
    private static final int MIN_SHAKE_ACCELERATION = 5;

    // Minimum number of movements to register a shake
    private static final int MIN_MOVEMENTS = 2;

    // Maximum time (in milliseconds) for the whole shake to occur
    private static final int MAX_SHAKE_DURATION = 500;

    // Arrays to store gravity and linear acceleration values
    private float[] mGravity = { 0.0f, 0.0f, 0.0f };
    private float[] mLinearAcceleration = { 0.0f, 0.0f, 0.0f };

    // Indexes for x, y, and z values
    private static final int X = 0;
    private static final int Y = 1;
    private static final int Z = 2;

    // OnShakeListener that will be notified when the shake is detected
    private OnShakeListener mShakeListener;

    // Start time for the shake detection
    long startTime = 0;

    // Counter for shake movements
    int moveCount = 0;

    // Constructor that sets the shake listener
    public ShakeDetector(OnShakeListener shakeListener) {
        mShakeListener = shakeListener;
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        // This method will be called when the accelerometer detects a change.

        // Call a helper method that wraps code from the Android developer site
        setCurrentAcceleration(event);

        // Get the max linear acceleration in any direction
        float maxLinearAcceleration = getMaxCurrentLinearAcceleration();

        // Check if the acceleration is greater than our minimum threshold
        if (maxLinearAcceleration > MIN_SHAKE_ACCELERATION) {
            long now = System.currentTimeMillis();

            // Set the startTime if it was reset to zero
            if (startTime == 0) {
                startTime = now;
            }

            long elapsedTime = now - startTime;

            // Check if we're still in the shake window we defined
            if (elapsedTime > MAX_SHAKE_DURATION) {
                // Too much time has passed. Start over!
                resetShakeDetection();
            }
            else {
                // Keep track of all the movements
                moveCount++;

                // Check if enough movements have been made to qualify as a shake
                if (moveCount > MIN_MOVEMENTS) {
                    // It's a shake! Notify the listener.
                    mShakeListener.onShake();

                    // Reset for the next one!
                    resetShakeDetection();
                }
            }
        }
    }

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

    private void setCurrentAcceleration(SensorEvent event) {
        /*
         *  BEGIN SECTION from Android developer site. This code accounts for 
         *  gravity using a high-pass filter
         */

        // alpha is calculated as t / (t + dT)
        // with t, the low-pass filter's time-constant
        // and dT, the event delivery rate

        final float alpha = 0.8f;

        // Gravity components of x, y, and z acceleration
        mGravity[X] = alpha * mGravity[X] + (1 - alpha) * event.values[X];
        mGravity[Y] = alpha * mGravity[Y] + (1 - alpha) * event.values[Y];
        mGravity[Z] = alpha * mGravity[Z] + (1 - alpha) * event.values[Z];

        // Linear acceleration along the x, y, and z axes (gravity effects removed)
        mLinearAcceleration[X] = event.values[X] - mGravity[X];
        mLinearAcceleration[Y] = event.values[Y] - mGravity[Y];
        mLinearAcceleration[Z] = event.values[Z] - mGravity[Z];

        /*
         *  END SECTION from Android developer site
         */
    }

    private float getMaxCurrentLinearAcceleration() {
        // Start by setting the value to the x value
        float maxLinearAcceleration = mLinearAcceleration[X];

        // Check if the y value is greater
        if (mLinearAcceleration[Y] > maxLinearAcceleration) {
            maxLinearAcceleration = mLinearAcceleration[Y];
        }

        // Check if the z value is greater
        if (mLinearAcceleration[Z] > maxLinearAcceleration) {
            maxLinearAcceleration = mLinearAcceleration[Z];
        }

        // Return the greatest value
        return maxLinearAcceleration;
    }

    private void resetShakeDetection() {
        startTime = 0;
        moveCount = 0;
    }

    // (I'd normally put this definition in it's own .java file)
    public interface OnShakeListener {
        public void onShake();
    }
}
Ben Jakuben
  • 3,127
  • 11
  • 60
  • 104
  • Hi! how i can set call after shake did you have any example work for this? – Muhammad Usman Ghani Mar 19 '14 at 05:06
  • I'm not sure what you mean by "after shake." The onShake() method gets called when the shake is detected. Do you want to wait until shaking is over? If so, you'd have to add a flag or something inside onSensorChanged() and change how and when resetShakeDetection() is called. – Ben Jakuben Mar 19 '14 at 13:27
  • @BenJakuben How can i verify this shaking pattern ? How can i store in SQLite database for future verification purpose? – sam_k Jun 01 '15 at 16:05
  • How to I get a count of shake in the `DoShake()` function? – Si8 Apr 13 '16 at 01:40
9

I really liked Peterdk's answer. I took it upon myself to make a coulpe of tweaks to his code .

file: ShakeDetector.java

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)
    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);
            }
        }
    }
}

Also, don't forget that you need to register an instance of the ShakeDetector with the SensorManager.

// 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) {
            handleShakeEvent(count); 
        }
    });

mSensorManager.registerListener(mShakeDetector, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
Akos Cz
  • 12,271
  • 1
  • 35
  • 31
  • 2
    Very nice addition. I was indeed thinking about doing something similar, but it was good enough for my client as it was. Great job! – Peterdk Jan 02 '13 at 22:02
  • 1
    I would change `SHAKE_THRESHOLD_GRAVITY` to 1.7F to prevent readers from thinking that this answer does not work because they have to shake quite hard due to the value set being **2.7F**. – ChuongPham Mar 28 '14 at 04:25
  • @Akos How can i verify this shaking pattern ? How can i store in SQLite database for future verification purpose? – sam_k Jun 01 '15 at 16:13
  • I would like to detect three shakes. Can it do that? – Si8 Apr 11 '16 at 20:25
  • 1
    the `count` variable in onShake tells you how many times the device was shaken – Akos Cz Apr 12 '16 at 20:37
  • 1
    Good job, I'd use `SystemClock.elapsedRealtime()`rather than `System.currentTimeMillis()` and initialize mShakeTimestamp with the same method rather than with 0 – Massimo Jan 30 '17 at 14:02
4

I have written a small example for detecting vertical and horizontal shakes and showing a Toast.

public class Accelerometerka2Activity extends Activity implements SensorEventListener { 
    private float mLastX, mLastY, mLastZ;
    private boolean mInitialized;
    private SensorManager mSensorManager;
    private Sensor mAccelerometer;
    private final float NOISE = (float) 8.0;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mInitialized = false;
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(this, mAccelerometer , SensorManager.SENSOR_DELAY_NORMAL);
    }

    protected void onResume() {
        super.onResume();
        mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
    }

    protected void onPause() {
        super.onPause();
        mSensorManager.unregisterListener(this);
    }


    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // can be safely ignored for this demo
    }


    public void onSensorChanged(SensorEvent event) {
        float x = event.values[0];
        float y = event.values[1];
        float z = event.values[2];
        if (!mInitialized) {
            mLastX = x;
            mLastY = y;
            mLastZ = z;
            mInitialized = true;
        } else {
            float deltaX = Math.abs(mLastX - x);
            float deltaY = Math.abs(mLastY - y);
            float deltaZ = Math.abs(mLastZ - z);
            if (deltaX < NOISE) deltaX = (float)0.0;
            if (deltaY < NOISE) deltaY = (float)0.0;
            if (deltaZ < NOISE) deltaZ = (float)0.0;
            mLastX = x;
            mLastY = y;
            mLastZ = z;
            if (deltaX > deltaY) {
                Toast.makeText(getBaseContext(), "Horizental", Toast.LENGTH_SHORT).show();
            } else if (deltaY > deltaX) {
                Toast.makeText(getBaseContext(), "Vertical", Toast.LENGTH_SHORT).show();
            }
        }
    }
}
Jon O
  • 6,267
  • 1
  • 41
  • 54
Attiq Rao
  • 51
  • 2
4

You can use seismic. An example can be found here.

Karoo
  • 503
  • 6
  • 18
4

I have tried several implementations, but would like to share my own. It uses G-force as unit for the threshold calculation. It makes it a bit easier to understand what is going on, and also with setting a good threshold.

It simply registers a increase in G force and triggers the listener if it exceeds the threshold. It doesn't use any direction thresholds, cause you don't really need that if you just want to register a good shake.

Of-course you need the standard registering and UN-registering of this listener in the Activity.

Also, to check what threshold you need, I recommend the following app (I am not in any way connected to that app)

    public class UmitoShakeEventListener implements SensorEventListener {

    /**
     * The gforce that is necessary to register as shake. (Must include 1G
     * gravity)
     */
    private final float shakeThresholdInGForce = 2.25F;

    private final float gravityEarth = SensorManager.GRAVITY_EARTH;

    private OnShakeListener listener;

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

    public interface OnShakeListener {
        public void onShake();
    }

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

    }

    @Override
    public void onSensorChanged(SensorEvent event) {

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

            float gX = x / gravityEarth;
            float gY = y / gravityEarth;
            float gZ = z / gravityEarth;

            //G-Force will be 1 when there is no movement. (gravity)
            float gForce = FloatMath.sqrt(gX * gX + gY * gY + gZ * gZ); 



            if (gForce > shakeThresholdInGForce) {
                listener.onShake();

            }
        }

    }

}
swiftBoy
  • 33,793
  • 26
  • 129
  • 124
Peterdk
  • 14,529
  • 18
  • 97
  • 136
  • it's probably more accurate, but at the expense of heavy calculations like sqrt and float divisions in OnSensorChanged... I'd go with the other ones if the application is already heavy! – rupps Jul 25 '14 at 13:37
  • I doubt a little float math is that heavy these days. Maybe when we were on Android 1.6 devices. – Peterdk Oct 07 '14 at 18:20
  • http://developer.android.com/training/articles/perf-tips.html#AvoidFloat ... and your routine is called some hundred times per second. An excellent battery eater! – rupps Oct 07 '14 at 20:08
  • I prefer readability above a very minor CPU usage increase. 2 x nothing is still not very much. But everyone his own preference. :) – Peterdk Oct 08 '14 at 00:04
  • sure, I prefer routines that run two times faster, it often pays off :P – rupps Oct 08 '14 at 01:49
4

I am developing a motion-detection and shake-detection app for my university project.

Besides the original target of the application, I am splitting the library part (responsible for motion and shake detection) from the app. The code is free, available on SourceForge with the project name "BenderCatch". Documentation I am producing will be ready around mid-september. http://sf.net/projects/bendercatch

It uses a more precise way to detect shake: watches BOTH the difference of force between SensorEvents AND the oscillations present in X and Y axis when you perform a shake. It can even make a sound (or vibrate) on each oscillation of the shake.

Feel free to ask me more by e-mail at raffaele [at] terzigno [dot] com

3
 package com.example.shakingapp;

import android.app.Activity;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;


public class MainActivity extends Activity implements SensorEventListener {
  private SensorManager sensorManager;
  private boolean color = false;
  private View view;
  private long lastUpdate;


/** Called when the activity is first created. */

  @Override
  public void onCreate(Bundle savedInstanceState) {
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN);

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    view = findViewById(R.id.textView);
    view.setBackgroundColor(Color.GREEN);

    sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
    lastUpdate = System.currentTimeMillis();
  }

  @Override
  public void onSensorChanged(SensorEvent event) {
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
      getAccelerometer(event);
    }

  }

  private void getAccelerometer(SensorEvent event) {
    float[] values = event.values;
    // Movement
    float x = values[0];
    float y = values[1];
    float z = values[2];

    System.out.println(x);
    System.out.println(y);
    System.out.println(z);
    System.out.println(SensorManager.GRAVITY_EARTH );

    float accelationSquareRoot = (x * x + y * y + z * z)
        / (SensorManager.GRAVITY_EARTH * SensorManager.GRAVITY_EARTH);

    long actualTime = System.currentTimeMillis();
    if (accelationSquareRoot >= 2) //
    {
      if (actualTime - lastUpdate < 200) {
        return;
      }
      lastUpdate = actualTime;
      Toast.makeText(this, "Device was shuffed "+accelationSquareRoot, Toast.LENGTH_SHORT)
          .show();
      if (color) {
        view.setBackgroundColor(Color.GREEN);

      } else {
        view.setBackgroundColor(Color.RED);
      }
      color = !color;
    }
  }

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

  }

  @Override
  protected void onResume() {
    super.onResume();
    // register this class as a listener for the orientation and
    // accelerometer sensors
    sensorManager.registerListener(this,
        sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
        SensorManager.SENSOR_DELAY_NORMAL);
  }

  @Override
  protected void onPause() {
    // unregister listener
    super.onPause();
    sensorManager.unregisterListener(this);
  }
} 
Muhammad Usman Ghani
  • 1,273
  • 13
  • 19
sachin pareek
  • 373
  • 6
  • 18
3
package anywheresoftware.b4a.student;

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

public class ShakeEventListener 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 int SHAKE_SLOP_TIME_MS = 500;
    private static final int SHAKE_COUNT_RESET_TIME_MS = 1000;

    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 + getSHAKE_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);
            }
        }
    }

    private long getSHAKE_SLOP_TIME_MS() {
        // TODO Auto-generated method stub
        return SHAKE_SLOP_TIME_MS;
    }

    public void setSHAKE_SLOP_TIME_MS(int sHAKE_SLOP_TIME_MS) {
        SHAKE_SLOP_TIME_MS = sHAKE_SLOP_TIME_MS;
    }   

}
3

Here is another code for this:

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;

   public class AccelerometerListener implements SensorEventListener {

        private SensorManager sensorManager;
        private List<Sensor> sensors;
        private Sensor sensor;
        private long lastUpdate = -1;
        private long currentTime = -1;
        private Main parent;
        private Timer timer;
        private int shakes;
        private static final Handler mHandler = new Handler();

        private float last_x, last_y, last_z;
        private float current_x, current_y, current_z, currenForce;
        private static final int FORCE_THRESHOLD = 500;
        private final int DATA_X = SensorManager.DATA_X;
        private final int DATA_Y = SensorManager.DATA_Y;
        private final int DATA_Z = SensorManager.DATA_Z;

        public AccelerometerListener(Main parent) {
            SensorManager sensorService = (SensorManager) parent
                    .getSystemService(Context.SENSOR_SERVICE);

            this.sensorManager = sensorService;
            if (sensorService == null)
                return;

            this.sensors = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
            if (sensors.size() > 0) {
                sensor = sensors.get(0);
            }

            this.parent = parent;
        }

        public void start() {
            if (sensor == null)
                return;

            sensorManager.registerListener(this, sensor,
                    SensorManager.SENSOR_DELAY_GAME);
        }

        public void stop() {
            if (sensorManager == null)
                return;

            sensorManager.unregisterListener(this);
        }

        public void onAccuracyChanged(Sensor s, int valu) {

        }

        public void onSensorChanged(SensorEvent event) {

            if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
                return;

            currentTime = System.currentTimeMillis();

            if ((currentTime - lastUpdate) > 50) {
                long diffTime = (currentTime - lastUpdate);
                lastUpdate = currentTime;

                current_x = event.values[DATA_X];
                current_y = event.values[DATA_Y];
                current_z = event.values[DATA_Z];

                currenForce = Math.abs(current_x + current_y + current_z - last_x
                        - last_y - last_z)
                        / diffTime * 10000;

                if (currenForce > FORCE_THRESHOLD) {
                    shakeDetected();
                }
                last_x = current_x;
                last_y = current_y;
                last_z = current_z;

            }
        }

        private void shakeDetected() {
            shakes++;

            if (shakes == 1) {
                if (timer != null) {
                    timer.cancel();
                }

                timer = new Timer();
                timer.schedule(new TimerTask() {

                    @Override
                    public void run() {
                        if (shakes > 3) {
                            mHandler.post(new Runnable() {

                                public void run() {
                                    // shake
                                }
                            });
                        }

                        shakes = 0;
                    }
                }, 500);
            }
        }
    }
peceps
  • 16,533
  • 10
  • 70
  • 77
2

Shaker.java

    import java.util.ArrayList;
    import android.content.Context;
    import android.hardware.Sensor;
    import android.hardware.SensorEvent;
    import android.hardware.SensorEventListener;
    import android.hardware.SensorManager;

    public class Shaker implements SensorEventListener{

        private static final String SENSOR_SERVICE = Context.SENSOR_SERVICE;
        private SensorManager sensorMgr;
        private Sensor mAccelerometer;
        private boolean accelSupported;
        private long timeInMillis;
        private long threshold;
        private OnShakerTreshold listener;
        ArrayList<Float> valueStack;

        public Shaker(Context context, OnShakerTreshold listener, long timeInMillis, long threshold) {
            try {
                this.timeInMillis = timeInMillis;
                this.threshold = threshold;
                this.listener = listener;
                if (timeInMillis<100){
                    throw new Exception("timeInMillis < 100ms");
                }
                valueStack = new ArrayList<Float>((int)(timeInMillis/100));
                sensorMgr = (SensorManager) context.getSystemService(SENSOR_SERVICE);
                mAccelerometer = sensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

            } catch (Exception e){
                e.printStackTrace();
            }
        }

        public void start() {
            try {
                accelSupported = sensorMgr.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME); 
                if (!accelSupported) {
                    stop();
                    throw new Exception("Sensor is not supported");
                }
            } catch (Exception e){
                e.printStackTrace();
            }
        }

        public void stop(){
            try {
                sensorMgr.unregisterListener(this, mAccelerometer);
            } catch (Exception e){
                e.printStackTrace();
            }
        }

        @Override
        protected void finalize() throws Throwable {
            try {
                stop();
            } catch (Exception e){
                e.printStackTrace();
            }
            super.finalize();
        }

        long lastUpdate = 0;
        private float last_x;
        private float last_y;
        private float last_z;

public void onSensorChanged(SensorEvent event) {
    try {
        if (event.sensor == mAccelerometer) {
            long curTime = System.currentTimeMillis();
            if ((curTime-lastUpdate)>getNumberOfMeasures()){

                lastUpdate = System.currentTimeMillis();
                float[] values = event.values;
                if (valueStack.size()>(int)getNumberOfMeasures())
                    valueStack.remove(0);
                float x = (int)(values[SensorManager.DATA_X]);
                float y = (int)(values[SensorManager.DATA_Y]);
                float z = (int)(values[SensorManager.DATA_Z]);
                float speed = Math.abs((x+y+z) - (last_x + last_y + last_z));

                valueStack.add(speed);

                String posText = String.format("X:%4.0f Y:%4.0f Z:%4.0f", (x-last_x), (y-last_y), (z-last_z));

                last_x = (x);
                last_y = (y);
                last_z = (z);

                float sumOfValues = 0;
                float avgOfValues = 0;

                for (float f : valueStack){
                        sumOfValues = (sumOfValues+f);
                }
                avgOfValues = sumOfValues/(int)getNumberOfMeasures();

                if (avgOfValues>=threshold){
                    listener.onTreshold();
                    valueStack.clear();
                }

                System.out.println(String.format("M: %+4d A: %5.0f V: %4.0f %s", valueStack.size(),avgOfValues,speed,posText));

            }
        }
    } catch (Exception e){
        e.printStackTrace();
    }
}


        private long getNumberOfMeasures() {
            return timeInMillis/100;
        }

        public void onAccuracyChanged(Sensor sensor, int accuracy) {}

        public interface OnShakerTreshold {
            public void onTreshold();
        }
    }

MainActivity.java

public class MainActivity extends Activity implements OnShakerTreshold{


    private Shaker s;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        s = new Shaker(getApplicationContext(), this, 5000, 20);
        // 5000 = 5 second of shaking
        // 20 = minimal threshold (very angry shaking :D)
        // beware screen rotation reset counter
    }

    @Override
    protected void onResume() {
        s.start();
        super.onResume();
    }

    @Override
    protected void onPause() {
        s.stop();
        super.onPause();
    }

    public void onTreshold() {
        System.out.println("FIRE LISTENER");
        RingtoneManager.getRingtone(getApplicationContext(), RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)).play();
    }


}

Have fun.

Mertuarez
  • 861
  • 7
  • 24
  • [link](http://stackoverflow.com/questions/456211/activity-restart-on-rotation-android) fix to "screen rotation reset counter" – Mertuarez Aug 08 '12 at 20:00
2
// Need to implement SensorListener
public class ShakeActivity extends Activity implements SensorListener {
// For shake motion detection.
private SensorManager sensorMgr;
private long lastUpdate = -1;
private float x, y, z;
private float last_x, last_y, last_z;
private static final int SHAKE_THRESHOLD = 800;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// start motion detection
sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
boolean accelSupported = sensorMgr.registerListener(this,
    SensorManager.SENSOR_ACCELEROMETER,
    SensorManager.SENSOR_DELAY_GAME);

if (!accelSupported) {
    // on accelerometer on this device
    sensorMgr.unregisterListener(this,
            SensorManager.SENSOR_ACCELEROMETER);
}
}

protected void onPause() {
if (sensorMgr != null) {
    sensorMgr.unregisterListener(this,
            SensorManager.SENSOR_ACCELEROMETER);
    sensorMgr = null;
    }
super.onPause();
}

public void onAccuracyChanged(int arg0, int arg1) {
// TODO Auto-generated method stub
}

public void onSensorChanged(int sensor, float[] values) {
if (sensor == SensorManager.SENSOR_ACCELEROMETER) {
    long curTime = System.currentTimeMillis();
    // only allow one update every 100ms.
    if ((curTime - lastUpdate)> 100) {
    long diffTime = (curTime - lastUpdate);
    lastUpdate = curTime;

    x = values[SensorManager.DATA_X];
    y = values[SensorManager.DATA_Y];
    z = values[SensorManager.DATA_Z];

    float speed = Math.abs(x+y+z - last_x - last_y - last_z)
                          / diffTime * 10000;
    if (speed > SHAKE_THRESHOLD) {
        // yes, this is a shake action! Do something about it!
    }
    last_x = x;
    last_y = y;
    last_z = z;
    }
}
}
}
sidhanshu
  • 346
  • 2
  • 9
2

You should subscribe as a SensorEventListener, and get the accelerometer data. Once you have it, you should monitor for sudden change in direction (sign) of acceleration on a certain axis. It would be a good indication for the 'shake' movement of device.

Lanki
  • 121
  • 1
  • 9
reflog
  • 7,368
  • 1
  • 39
  • 46
1

Working with me v.good Reference

public class ShakeEventListener implements SensorEventListener {
public final static int SHAKE_LIMIT = 15;
public final static int LITTLE_SHAKE_LIMIT = 5;

private SensorManager mSensorManager;
private float mAccel = 0.00f;
private float mAccelCurrent = SensorManager.GRAVITY_EARTH;
private float mAccelLast = SensorManager.GRAVITY_EARTH;

private ShakeListener listener;

public interface ShakeListener {
    public void onShake();
    public void onLittleShake();
}

public ShakeEventListener(ShakeListener l) {
    Activity a = (Activity) l;
    mSensorManager = (SensorManager) a.getSystemService(Context.SENSOR_SERVICE);
    listener = l;
    registerListener();
}

public ShakeEventListener(Activity a, ShakeListener l) {
    mSensorManager = (SensorManager) a.getSystemService(Context.SENSOR_SERVICE);
    listener = l;
    registerListener();
}

public void registerListener() {
    mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
}

public void unregisterListener() {
    mSensorManager.unregisterListener(this);
}

public void onSensorChanged(SensorEvent se) {
    float x = se.values[0];
    float y = se.values[1];
    float z = se.values[2];
    mAccelLast = mAccelCurrent;
    mAccelCurrent = (float) FloatMath.sqrt(x*x + y*y + z*z);
    float delta = mAccelCurrent - mAccelLast;
    mAccel = mAccel * 0.9f + delta;
    if(mAccel > SHAKE_LIMIT)
        listener.onShake();
    else if(mAccel > LITTLE_SHAKE_LIMIT)
        listener.onLittleShake();
}

public void onAccuracyChanged(Sensor sensor, int accuracy) {}
}
Khaled Lela
  • 5,495
  • 5
  • 39
  • 63
0

You might want to try open source tinybus. With it shake detection is as easy as this.

public class MainActivity extends Activity {

    private Bus mBus;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...

        // Create a bus and attach it to activity
        mBus = TinyBus.from(this).wire(new ShakeEventWire());
    }

    @Subscribe
    public void onShakeEvent(ShakeEvent event) {
        Toast.makeText(this, "Device has been shaken", 
                Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mBus.register(this);
    }

    @Override
    protected void onStop() {
        mBus.unregister(this);
        super.onStop();
    }
}

It uses seismic for shake detection.

sergej shafarenka
  • 19,464
  • 6
  • 62
  • 82