How to Build a Step Counting Application in Android Studio?
Many of us have used the step counter on our phones while we go for walk or run. It counts the total steps that the user has walked and displays it on the screen. The other name for the step counter is a pedometer. But have you ever thought about how can an app count our steps? What is the coding behind the working of this app? Let’s find the answer to these questions by making one.
What are we going to build in this article?
We will be building an application that displays the steps that the user has walked. We will use TextView in our XML file which will show the step count and a heading on the screen, and one ImageView for displaying the circle around the text. When the user will tap for a long time on the screen it will get reset to 0. A sample GIF is given below to get an idea about what we are going to do in this article. Note that we are going to implement the application using the Kotlin language.
Step by Step Implementation
Step 1: Create a New Project
To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Kotlin as the programming language.
Step 2: Working with AndroidManifest.xml for taking permission from the user
Navigate to the app/manifests/AndroidManifest.xml and write the code given below in the manifest for taking the user permission for Activity Recognition:
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>
Step 3: Adding a drawable resource file
Navigate to app/res/drawable and right-click on the drawable folder and go to New/Drawable Resource File.
Name the file as the circle and with the default settings, click on the ok button.
Step 4: Working with drawable resource file
In this step, we will add the code to the resource file. We are making a circular stroke to use it, in the ImageView in the main XML file of the app. Below is the code for the circle.xml resource file that we made It in the previous step.
XML
<? xml version = "1.0" encoding = "utf-8" ?> < item > <!-- creates an oval shape --> < shape android:shape = "oval" > <!-- giving the color and width to the stroke --> < stroke android:color = "#0F9D58" android:width = "3dp" /> <!-- giving the width and the height to the shape --> < size android:width = "120dp" android:height = "120dp" /> </ shape > </ item > </ selector > |
Step 5: Working with the activity_main.xml file
Navigate to the app/res/layout/activity_main.xml and add the below code to that file. Below is the code for the activity_main.xml file.
XML
<? xml version = "1.0" encoding = "utf-8" ?> < androidx.constraintlayout.widget.ConstraintLayout android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = ".MainActivity" > <!--Text View for the "Steps" displayed--> < TextView android:id = "@+id/steps" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_marginEnd = "8dp" android:layout_marginStart = "8dp" android:layout_marginTop = "290dp" android:text = "Steps" android:textSize = "45sp" android:textColor = "@color/black" app:layout_constraintEnd_toEndOf = "parent" app:layout_constraintStart_toStartOf = "parent" app:layout_constraintTop_toTopOf = "parent" /> <!--Text View for the step count--> < TextView android:id = "@+id/tv_stepsTaken" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_marginEnd = "8dp" android:layout_marginStart = "8dp" android:layout_marginTop = "30dp" android:text = "0" android:textSize = "37sp" android:textColor = "@color/black" app:layout_constraintEnd_toEndOf = "parent" app:layout_constraintStart_toStartOf = "parent" app:layout_constraintTop_toBottomOf = "@+id/steps" /> <!--Image View for the circle--> < ImageView android:layout_width = "200dp" android:layout_height = "200dp" android:layout_marginTop = "264dp" android:background = "@drawable/circle" app:layout_constraintEnd_toEndOf = "parent" app:layout_constraintHorizontal_bias = "0.497" app:layout_constraintStart_toStartOf = "parent" app:layout_constraintTop_toTopOf = "parent" /> </ androidx.constraintlayout.widget.ConstraintLayout > |
Step 6: Working with the MainActivity.kt file
Go to the MainActivity.kt file and refer to the following code. Below is the code for the MainActivity.kt file. Comments are added inside the code to understand the code in more detail.
Kotlin
import android.content.Context import android.hardware.Sensor import android.hardware.SensorEvent import android.hardware.SensorEventListener import android.hardware.SensorManager import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import android.widget.TextView import android.widget.Toast class MainActivity : AppCompatActivity(), SensorEventListener { // Added SensorEventListener the MainActivity class // Implement all the members in the class MainActivity // after adding SensorEventListener // we have assigned sensorManger to nullable private var sensorManager: SensorManager? = null // Creating a variable which will give the running status // and initially given the boolean value as false private var running = false // Creating a variable which will counts total steps // and it has been given the value of 0 float private var totalSteps = 0f // Creating a variable which counts previous total // steps and it has also been given the value of 0 float private var previousTotalSteps = 0f override fun onCreate(savedInstanceState: Bundle?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_main) loadData() resetSteps() // Adding a context of SENSOR_SERVICE as Sensor Manager sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager } override fun onResume() { super .onResume() running = true // Returns the number of steps taken by the user since the last reboot while activated // This sensor requires permission android.permission.ACTIVITY_RECOGNITION. // So don't forget to add the following permission in AndroidManifest.xml present in manifest folder of the app. val stepSensor = sensorManager?.getDefaultSensor(Sensor.TYPE_STEP_COUNTER) if (stepSensor == null ) { // This will give a toast message to the user if there is no sensor in the device Toast.makeText( this , "No sensor detected on this device" , Toast.LENGTH_SHORT).show() } else { // Rate suitable for the user interface sensorManager?.registerListener( this , stepSensor, SensorManager.SENSOR_DELAY_UI) } } override fun onSensorChanged(event: SensorEvent?) { // Calling the TextView that we made in activity_main.xml // by the id given to that TextView var tv_stepsTaken = findViewById<TextView>(R.id.tv_stepsTaken) if (running) { totalSteps = event!!.values[ 0 ] // Current steps are calculated by taking the difference of total steps // and previous steps val currentSteps = totalSteps.toInt() - previousTotalSteps.toInt() // It will show the current steps to the user tv_stepsTaken.text = ( "$currentSteps" ) } } fun resetSteps() { var tv_stepsTaken = findViewById<TextView>(R.id.tv_stepsTaken) tv_stepsTaken.setOnClickListener { // This will give a toast message if the user want to reset the steps Toast.makeText( this , "Long tap to reset steps" , Toast.LENGTH_SHORT).show() } tv_stepsTaken.setOnLongClickListener { previousTotalSteps = totalSteps // When the user will click long tap on the screen, // the steps will be reset to 0 tv_stepsTaken.text = 0 .toString() // This will save the data saveData() true } } private fun saveData() { // Shared Preferences will allow us to save // and retrieve data in the form of key,value pair. // In this function we will save data val sharedPreferences = getSharedPreferences( "myPrefs" , Context.MODE_PRIVATE) val editor = sharedPreferences.edit() editor.putFloat( "key1" , previousTotalSteps) editor.apply() } private fun loadData() { // In this function we will retrieve data val sharedPreferences = getSharedPreferences( "myPrefs" , Context.MODE_PRIVATE) val savedNumber = sharedPreferences.getFloat( "key1" , 0f) // Log.d is used for debugging purposes Log.d( "MainActivity" , "$savedNumber" ) previousTotalSteps = savedNumber } override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { // We do not have to write anything in this function for this app } } |
Now run the app and see the output of the code below:
Output:
Note: We have to allow the permission required for the app by going to app settings and then enabling it. It will not count the steps in the emulator, you have to use a real android device to test it.