Android Shake Detection Tutorial Example Sensor Listener Accelerometer

android shake detection, android custom dialog with transparent background

Android shake detection tutorial with example is what you will learn today.

System of Android gives facility to detect motion sensor listener with accelerometer class.

You can define various code on shake function of your android device that will work when user shakes the device.

It is something that is unique way to stand out from old traditional methods like button click.

For example, you are making a saloon booking application. Now you can have a feature like when the user shakes his device system will return near by saloons.

In this tutorial, we will see how to detect when user shake his android device. We will change the text of the TextView on the the shake function detection.

We can also do this task with the help of background service. Detection service will constantly watch out for shake activity and it will run specific code when it finds it.

I have used android studio to develop this tutorial example.

Demo Video

Following video demonstrate the output of this example.

Two Ways

You can implement shake feature in two ways.

First way is to have one detection class that we will use to detect the shake in any particular activity.

This method can not be used in that way that you write once and use everywhere. You have to write detection code in every activity in this method.

Second method is to create a background service. Make sensor registration in this service. This service will constantly check for shake action.

Here, you do not need to write sensor registration code everywhere. Just create one service and start it in any activity. That’s it.

We will implement both this feature in this example.

So let us generate shake example step by step.

Step 1. Detector Class

Let us make a class that will detect a shake task of the user.

Create a new class and give it a name “ShakeDetector.java”

Copy and paste the below source code in “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).
     * 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 = (float)Math.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);
            }
        }
    }
}

Step 2. Detect Service

We are also implementing one feature in which a background service is running constantly.

Whenever the user will shake his android device, this service detect this and will execute specific code.

So for developing this service, make a new java class named “ShakeService.java”

Write down the following coding lines in “ShakeService.java”

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import java.util.Random;

public class ShakeService extends Service implements SensorEventListener {

    private SensorManager mSensorManager;
    private Sensor mAccelerometer;
    private float mAccel; // acceleration apart from gravity
    private float mAccelCurrent; // current acceleration including gravity
    private float mAccelLast; // last acceleration including gravity

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mAccelerometer = mSensorManager
                .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(this, mAccelerometer,
                SensorManager.SENSOR_DELAY_UI, new Handler());
        return START_STICKY;
    }

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

    @Override
    public void onSensorChanged(SensorEvent event) {
        float x = event.values[0];
        float y = event.values[1];
        float z = event.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

        if (mAccel > 11) {
            Random rnd = new Random();
            int color = Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
            ServiceActivity.tvShakeService.setText("Service detects the Shake Action!! Color is also changed..!!!");
            ServiceActivity.tvShakeService.setTextColor(color);
        }
    }

}

Consider the below source code

 if (mAccel > 11) {
            Random rnd = new Random();
            int color = Color.argb(255, rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256));
            ServiceActivity.tvShakeService.setText("Service detects the Shake Action!! Color is also changed..!!!");
            ServiceActivity.tvShakeService.setTextColor(color);
        }

Compiler will execute the above code when service detects the shake action.

It will create a random color first and then it will set this color as a text color of the textview.

This textview is present in the ServiceActivity which we will create in the next step.

If you probably know that every activity is defined in the manifest by the compiler when you create them.

Similarly, we also need to define this service in the AndroidManifest.xml file.

Add following line in AndroidManifest.xml file (between <application>…</application> tags).

 <service android:name="com.example.parsaniahardik.shakedetection.ShakeService"></service>

Final code for AndroidManifest.xml file looks like the below

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.parsaniahardik.shakedetection">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".ServiceActivity"></activity>
        <service android:name="com.example.parsaniahardik.shakedetection.ShakeService"></service>
    </application>

</manifest>

Step 3. Activity to Start Service

Now we need to make one activity in which we will start the background service.

Create a new activity named “ServiceActivity”

Add the below source code in activity_service.xml file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:gravity="center"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ServiceActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/tvShakeService"
        android:layout_marginTop="20dp"
        android:textSize="30sp"
        android:textColor="@color/colorAccent"/>

</LinearLayout>

I have taken one TextView in this file.

We will change the text color on every shake detection.

Copy the following source code in ServiceActivity.java class

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

public class ServiceActivity extends AppCompatActivity {

    public static TextView tvShakeService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_service);

        Intent intent = new Intent(this, ShakeService.class);
        //Start Service
        startService(intent);

        tvShakeService = findViewById(R.id.tvShakeService);

    }
}

Following code will start the background service

 Intent intent = new Intent(this, ShakeService.class);
 //Start Service
 startService(intent);

Step 4. Changing Main Activity

Now last thing is to change the code for Main Activity files.

Write the below code in activity_main.xml file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:gravity="center"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/btn"
        android:textSize="20sp"
        android:text="Goto Next Activity and detect shake with background service"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/tvShake"
        android:layout_marginTop="20dp"
        android:textSize="30sp"
        android:textColor="@color/colorAccent"
        android:text="Please Shake Your Device!!!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</LinearLayout>

Here I have taken one button and one textview.

Write the following code in MainActivity.java file

import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

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

    private TextView tvShake;
    private Button btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {   //demo link   http://jasonmcreynolds.com/?p=388
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tvShake = findViewById(R.id.tvShake);
        btn = findViewById(R.id.btn);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this,ServiceActivity.class);
                startActivity(intent);
            }
        });

        // ShakeDetector initialization
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mAccelerometer = mSensorManager
                .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mShakeDetector = new ShakeDetector();
        mShakeDetector.setOnShakeListener(new ShakeDetector.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.
                 */
                tvShake.setText("Shake Action is just detected!!");
                Toast.makeText(MainActivity.this, "Shaked!!!", Toast.LENGTH_SHORT).show();
            }
        });

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

Let us understand the above code.

Look at the below source code

   btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this,ServiceActivity.class);
                startActivity(intent);
            }
        });

We will open a ServiceActivity on the button click.

Consider the following code

 // ShakeDetector initialization
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mAccelerometer = mSensorManager
                .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mShakeDetector = new ShakeDetector();
        mShakeDetector.setOnShakeListener(new ShakeDetector.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.
                 */
                tvShake.setText("Shake Action is just detected!!");
                Toast.makeText(MainActivity.this, "Shaked!!!", Toast.LENGTH_SHORT).show();
            }
        });

Above code will initialize the detection through the ShakeDector class.

Whenever the system detects the Shake action, it will execute the onShake() method.

onShake() method will update the text of the TextView.

Then it will show a toast saying “Shaked!”

Now give your attention to the below code

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

Generally, compiler call the onResume() method when activity is opening for first time or it is returning from another application.

Compiler will register the sensor manager in this method.

See the following source code

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

System will run the onPause() method when the activity is destroyed or user directly moves to another application without killing our app.

Compiler will cancel the registration of sensor in this method.

Download Source Code for Android Shake Detection Example

[sociallocker]Download Shake Detection Example Source Code[/sociallocker]