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!!!”