Kotlin Detect Shake Event | Shake Detection Sensor Android

best android development course on udemy, google map in fragment kotlin, show current location on map kotlin, Kotlin Volley JSON Parsing, Kotlin Detect Shake, kotlin google map

Welcome to Kotlin Detect Shake Event in Android Example tutorial.

This example will guide you how to detect the shake event of the android device in kotlin.

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

Shaking of device is the new approach compared to something like button click.

You can define any code or feature when the user shakes the android device.

We will also create one background service which will continuously check out for shake detection event.

Service will run the specific code when it detects the shake event using kotlin.

First of all, check the below video for main idea on shake detection.

Detect Class

To create the shake detection event, we need to make one class.

This class will help us to detect shake event of android device.

So create a new class and give it a name like ShakeDetector.kt

You should add the following source code lines into the ShakeDetector.kt file.

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

/**
 * Created by zerones-hardik on 22/7/16.
 */
class ShakeDetector : SensorEventListener {

    private var mListener: OnShakeListener? = null
    private var mShakeTimestamp: Long = 0
    private var mShakeCount: Int = 0

    fun setOnShakeListener(listener: OnShakeListener) {
        this.mListener = listener
    }

    interface OnShakeListener {
        fun onShake(count: Int)
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
        // ignore
    }

    override fun onSensorChanged(event: SensorEvent) {

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

            val gX = x / SensorManager.GRAVITY_EARTH
            val gY = y / SensorManager.GRAVITY_EARTH
            val gZ = z / SensorManager.GRAVITY_EARTH

            // gForce will be close to 1 when there is no movement.
            val gForce = Math.sqrt((gX * gX + gY * gY + gZ * gZ).toDouble()).toFloat()

            if (gForce > SHAKE_THRESHOLD_GRAVITY) {
                val 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)
            }
        }
    }

    companion object {

        /*
     * 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 val SHAKE_THRESHOLD_GRAVITY = 2.7f
        private val SHAKE_SLOP_TIME_MS = 500
        private val SHAKE_COUNT_RESET_TIME_MS = 3000
    }
}

This class in implementing the SensorEventListener interface to detect shake event.

Whenever the user is shaking the android device, sensors will do their work and this class will detect shake event with the help of the sensors.

On the detection of shake sensor, compiler will run the onSensorChanged() method.

Detect Service

After detect class, let us make a service for detection event of android device.

Make a new Kotlin class and set it’s name as the ShakeService.kt

Following is the code snippet for the ShakeService.kt file.

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

class ShakeService : Service(), SensorEventListener {

    private var mSensorManager: SensorManager? = null
    private var mAccelerometer: Sensor? = null
    private var mAccel: Float = 0.toFloat() // acceleration apart from gravity
    private var mAccelCurrent: Float = 0.toFloat() // current acceleration including gravity
    private var mAccelLast: Float = 0.toFloat() // last acceleration including gravity

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        mSensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        mAccelerometer = mSensorManager!!
            .getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        mSensorManager!!.registerListener(
            this, mAccelerometer,
            SensorManager.SENSOR_DELAY_UI, Handler()
        )
        return Service.START_STICKY
    }

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}

    override fun onSensorChanged(event: SensorEvent) {
        val x = event.values[0]
        val y = event.values[1]
        val z = event.values[2]
        mAccelLast = mAccelCurrent
        mAccelCurrent = Math.sqrt((x * x + y * y + z * z).toDouble()).toFloat()
        val delta = mAccelCurrent - mAccelLast
        mAccel = mAccel * 0.9f + delta // perform low-cut filter

        if (mAccel > 11) {
            val rnd = Random()
            val 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)
        }
    }

}

Above code will create one background service. It means that compiler will look for above code continuously in the background.

Whenever the shake event takes place, compiler will call onSensorChanged() method.

Now focus on the below if condition.

 if (mAccel > 11) {
            val rnd = Random()
            val 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)
        }

In this condition, compiler will generate the random color first.

After generating the color, compiler will set the text value inside the text view which we will define in the ServiceActivity later.

After setting the text view, compiler will also change the color of the text which it had generated.

Service In Manifest

Now go to your AndroidManifest.xml file and add the following line inside <application> tag.

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

Above line is defining the background service (ShakeService.kt) in the manifest.

So final code snippet for AndroidManifest.xml file is as the following

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

    <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=".ServiceActivity">
        </activity>
        <service android:name="com.example.shakedetectionkotlin.ShakeService"></service>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

Service Activity

Now it is time to make an activity which will help us to turn on the background service.

Make a new activity with the name as ServiceActivity

ServiceActivity.kt should contain the following coding lines

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

class ServiceActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_service)

        val intent = Intent(this, ShakeService::class.java)
        //Start Service
        startService(intent)

        tvShakeService = findViewById(R.id.tvShakeService)

    }

    companion object {

        lateinit var tvShakeService: TextView
    }
}

Look at the above code. Compiler will create one Intent with ShakeService.kt file.

Thus our background service will start in this activity.

Here, compiler will initializing one text view object (tvShakeService) and it is companion object.

That means that we can access this text view object (tvShakeService) from other Kotlin classes also.

We have used this object inside the ShakeService.kt class.

activity_service.xml file should have the following lines

<?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>

Above layout XML file has just one Text View. Compiler will change the color of the above text view values on the shake event.

Final Code

There are two main files in every android studio project.

One is activity_main.xml and another is MainActivity.kt

Your activity_main.xml file should have the below coding lines

<?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>

Above file has one button and one text view.

Text view will change it’s value when the user shakes the android device.

Now write down the below code structure inside the MainActivity.kt 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.widget.Button
import android.widget.TextView
import android.widget.Toast

class MainActivity : AppCompatActivity() {

    private var mSensorManager: SensorManager? = null
    private var mAccelerometer: Sensor? = null
    private var mShakeDetector: ShakeDetector? = null
    private var tvShake: TextView? = null
    private var btn: Button? = null

    override fun onCreate(savedInstanceState: Bundle?) {   //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 {
            val intent = Intent(this@MainActivity, ServiceActivity::class.java)
            startActivity(intent)
        }

        // ShakeDetector initialization
        mSensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
        mAccelerometer = mSensorManager!!
            .getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        mShakeDetector = ShakeDetector()
        mShakeDetector!!.setOnShakeListener(object : ShakeDetector.OnShakeListener {

            override fun onShake(count: Int) {
                /*
                 * 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!!.text = "Shake Action is just detected!!"
                Toast.makeText(this@MainActivity, "Shaked!!!", Toast.LENGTH_SHORT).show()
            }
        })

    }

    public override fun onResume() {
        super.onResume()
        // Add the following line to register the Session Manager Listener onResume
        mSensorManager!!.registerListener(mShakeDetector, mAccelerometer, SensorManager.SENSOR_DELAY_UI)
    }

    public override fun onPause() {
        // Add the following line to unregister the Sensor Manager onPause
        mSensorManager!!.unregisterListener(mShakeDetector)
        super.onPause()
    }
}

First of all, attend the below code lines

 private var mSensorManager: SensorManager? = null
    private var mAccelerometer: Sensor? = null
    private var mShakeDetector: ShakeDetector? = null
    private var tvShake: TextView? = null
    private var btn: Button? = null

Compiler will create the objects for the SensorManager, Sensor, ShakeDetector, Text View and Button classes.

Now read the following source lines

   btn!!.setOnClickListener {
            val intent = Intent(this@MainActivity, ServiceActivity::class.java)
            startActivity(intent)
        }

When the user clicks the button, compiler will run the above code.

It will start Service Activity on the button click.

Whenever the shake event occurs, compiler will run the below

 override fun onShake(count: Int) {
                /*
                 * 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!!.text = "Shake Action is just detected!!"
                Toast.makeText(this@MainActivity, "Shaked!!!", Toast.LENGTH_SHORT).show()
            }

It will change the text of the text view.

It will also create one Toast saying “Shaked!!!”

Get Code From Github

Hit me to download Code