Android Step Counting Application in kotlin

In this post, we will create a basic step counting application using a step counter sensor.

Step counter sensor

  • The step counter sensor returns the number of steps taken by the user since the last reboot while the sensor was activated.
  • It is ideal for fitness tracking applications.
  • The value is returned as a float and is reset to zero only on a system reboot.
  • This sensor requires permission android.permission.ACTIVITY_RECOGNITION which allows an application to recognize the physical activity.

Creating new project

1 . Create a new project by going to File ⇒ New Android Project, select Empty Activity, provide app name, select language to kotlin and then finally click on finish.

Adding Permission

2 . Open the AndroidManifest.xml file and add the below permission above application tag.

AndroidManifest.xml

For app targets Android 9 (API level 28) or below,

  • Add the permission to the manifest file.
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>

For app targets Android 10 (API level 29) or higher, we must request permission from the user at runtime, and add the above permission in the application manifest file:

Request permission at runtime
  • Check if the permission is granted:
if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.ACTIVITY_RECOGNITION)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
}
  • If permission isn’t already granted, request the permission:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACTIVITY_RECOGNITION),
ACTIVITY_RECOGNITION_REQUEST_CODE
)
}
The Layout File 

3 . The below layout file contains two textviews in which tv_stepsTaken textview, shows the no of steps taken by the user.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/background"
android:padding="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">

<TextView
android:id="@+id/steps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="Steps"
android:textColor="@color/black"
android:textSize="45sp" />

<TextView
android:id="@+id/tv_stepsTaken"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/steps"
android:layout_centerHorizontal="true"
android:text="0"
android:textColor="@color/black"
android:textSize="35sp" />

</RelativeLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

4 . Create an instance of the default step counter sensor and sensorManager (lets you access the device’s sensors) :

//initializing sensorManager instance
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager

//TYPE_STEP_COUNTER: A constant describing a step counter sensor
val stepSensor = sensorManager?.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)

5 . Now register listener with sensor manager, if the instance of the step counter sensor is not null.

if (stepSensor == null) {
// show toast message, if there is no sensor in the device
Toast.makeText(this, "No sensor detected on this device", Toast.LENGTH_SHORT).show()
} else {
// register listener with sensorManager
sensorManager?.registerListener(this, stepSensor, SensorManager.SENSOR_DELAY_UI)
}

6 . Implement SensorEventListener and override the below methods as shown below:

SensorEventListener: Used for receiving notifications from the SensorManager when there is new sensor data.

  • onSensorChanged(): Called when there is a new sensor event.
  • onAccuracyChanged(): Called when the accuracy of the registered sensor has changed.
override fun onSensorChanged(event: SensorEvent?) {

// get textview by its id
var tv_stepsTaken = findViewById<TextView>(R.id.tv_stepsTaken)

if (running) {

//get the number of steps taken by the user.
totalSteps = event!!.values[0]

val currentSteps = totalSteps.toInt()

// set current steps in textview
tv_stepsTaken.text = ("$currentSteps")
}
}

override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
println("onAccuracyChanged: Sensor: $sensor; accuracy: $accuracy")
}

Complete MainActivity code

MainActivity.kt

package com.c1ctech.stepscountapp

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat

class MainActivity : AppCompatActivity(), SensorEventListener {

private var sensorManager: SensorManager? = null

// variable gives the running status
private var running = false

// variable counts total steps
private var totalSteps = 0f

val ACTIVITY_RECOGNITION_REQUEST_CODE = 100

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

//check if permission isn't already granted, request the permission
if (isPermissionGranted()) {
requestPermission()
}

//initializing sensorManager instance
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
}

override fun onResume() {

super.onResume()
running = true

// TYPE_STEP_COUNTER: A constant describing a step counter sensor
// Returns the number of steps taken by the user since the last reboot while activated
// This sensor requires permission android.permission.ACTIVITY_RECOGNITION.
val stepSensor = sensorManager?.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)

if (stepSensor == null) {
// show toast message, if there is no sensor in the device
Toast.makeText(this, "No sensor detected on this device", Toast.LENGTH_SHORT).show()
} else {
// register listener with sensorManager
sensorManager?.registerListener(this, stepSensor, SensorManager.SENSOR_DELAY_UI)
}
}

override fun onPause() {
super.onPause()
running = false
// unregister listener
sensorManager?.unregisterListener(this)
}

override fun onSensorChanged(event: SensorEvent?) {

// get textview by its id
var tv_stepsTaken = findViewById<TextView>(R.id.tv_stepsTaken)

if (running) {

//get the number of steps taken by the user.
totalSteps = event!!.values[0]

val currentSteps = totalSteps.toInt()

// set current steps in textview
tv_stepsTaken.text = ("$currentSteps")
}
}

override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
println("onAccuracyChanged: Sensor: $sensor; accuracy: $accuracy")
}

private fun requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.ACTIVITY_RECOGNITION),
ACTIVITY_RECOGNITION_REQUEST_CODE
)
}
}

private fun isPermissionGranted(): Boolean {
return ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACTIVITY_RECOGNITION
) != PackageManager.PERMISSION_GRANTED
}

//handle requested permission result(allow or deny)
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
ACTIVITY_RECOGNITION_REQUEST_CODE -> {
if ((grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED)
) {
// Permission is granted. Continue the action or workflow
// in your app.
}
}
}
}
}

When you run the app it will look like this as shown below:

       

Leave a Reply