Open In App

Activity Recognition in Android

Last Updated : 15 Oct, 2021
Improve
Improve
Like Article
Like
Save
Share
Report

In this article, we are going to learn about an API called the Activity Recognition Transition API or Transition API  which is intended for detecting the activity of a particular user such as driving, walking or running, etc. There are many applications that use this Activity Recognition Transition API. For example, a kilometer finder app starts running when you start driving a car or a bike and stops when you stop driving. Other examples of this Activity Recognition can be any Health and Fitness app that determines how many meters or kilometers you are running or walking on a particular day and after that, you can find the calories burnt on that day and much more.

Here we are going to make an application that will detect the user’s activity like still, running, walking, driving, or something else. A sample screenshot image is given below to get an idea about what we are going to do in this article. 

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: Add dependencies

// Google Play Service’s Location

implementation ‘com.google.android.gms:play-services-location:18.0.0’

implementation “androidx.preference:preference-ktx:1.1.1”

// Timber

implementation ‘com.jakewharton.timber:timber:4.7.1’

// Notification Library

implementation “io.karn:notify:1.3.0”

// Easy Permissions

implementation ‘pub.devrel:easypermissions:3.0.0’

// Material Design

implementation ‘com.google.android.material:material:1.5.0-alpha02’

Step 3: Add a reference to the maven repository in the top-level build.gradle

allprojects {

   repositories {

       …

       // For Notify library

       maven { url ‘https://jitpack.io’ }

   }

}

Step 4: Working with AndroidMenifest.xml

Make sure to add below permissions 

  <!– Required for 28 and below. –>

  <uses-permission android:name=”com.google.android.gms.permission.ACTIVITY_RECOGNITION” />

  <!– Required for 29+. –>

   <uses-permission android:name=”android.permission.ACTIVITY_RECOGNITION” />

<application >

….

<activity >

….

</activity>

<receiver

          android:name=”.ActivityTransitionReceiver”

          android:exported=”false”

          android:permission=”com.google.android.gms.permission.ACTIVITY_RECOGNITION”>

          <intent-filter>

              <action android:name=”action.TRANSITIONS_DATA” />

          </intent-filter>

      </receiver>

</application>

Step 5: Adding the UI of the application.

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
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
  
    <com.google.android.material.switchmaterial.SwitchMaterial
        android:id="@+id/switchActivityTransition"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Activity Transition"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
  
</androidx.constraintlayout.widget.ConstraintLayout>


Step 6: Create Constant.kt class and write all the required constants here

Kotlin




// declare all required constants at one place
object Constants {
    const val REQUEST_CODE_ACTIVITY_TRANSITION = 123
    const val REQUEST_CODE_INTENT_ACTIVITY_TRANSITION = 122
    const val ACTIVITY_TRANSITION_NOTIFICATION_ID = 111
    const val ACTIVITY_TRANSITION_STORAGE = "ACTIVITY_TRANSITION_STORAGE"
}


In Android, we have the Activity Recognition Client that wakes up your device at a regular interval and afterward gathers the information from the gadget’s sensor and after that this gathered information will be utilized to decide the Activities with the assistance of some Machine Learning calculation. You should simply utilize the Activity Recognition Client and the API will wrap up for you no compelling reason to learn ML.

 Following are the activities that can be detected by the Activity Recognition Client:

  • STILL: When the mobile device will be still when the user is either sitting at someplace or the mobile device is having no motion, then the Activity Recognition Client will detect the STILL activity.
  • ON_FOOT: When the mobile device is moving at a normal speed i.e. the user carrying the mobile device is either walking or running then the Activity Recognition Client will detect the ON_FOOT activity.
  • WALKING: This is a sub-activity of the ON_FOOT activity which is detected by the Activity Recognition Client when the user carrying the mobile device is walking.
  • RUNNING: This is also a sub-activity of ON_FOOT activity which is detected by the Activity Recognition Client when the user carrying the mobile device is running.
  • IN_VEHICLE: This activity is detected when the mobile device is in the bus or car or some other kind of vehicle or the user holding the mobile device is present in the vehicle.
  • ON_BICYCLE: When the device is on the bicycle or the user carrying the mobile is on a bicycle then this activity will be detected.
  • TILTING: When the mobile device is being lifted and is having some angle with the flat surface then the Activity Recognition Client will detect this activity.
  • UNKNOWN: The Activity Recognition Client will show this result when the device is unable to detect any activity on the mobile device.

Step 7: Create ActivityTransitionsUtil.kt class that will contain a list of activities types and transition types

To start receiving notifications about activity transitions we must build an ActivityTransitionRequest object that determines the type of activity and transition. For this, we must create a list of ActivityTransition objects and pass it to the ActivityTransitionRequest class. Below is the complete code of the ActivityTransitionsUtil.kt class. Comments are added inside the code to understand each line of the code.

Kotlin




import android.Manifest
import android.content.Context
import android.os.Build
import androidx.annotation.RequiresApi
import com.google.android.gms.location.ActivityTransition
import com.google.android.gms.location.ActivityTransitionRequest
import com.google.android.gms.location.DetectedActivity
import pub.devrel.easypermissions.EasyPermissions
  
object ActivityTransitionsUtil {
    private fun getTransitions(): MutableList<ActivityTransition> {
  
        // List of activity transitions to track
        val transitions = mutableListOf<ActivityTransition>()
  
        transitions +=
            ActivityTransition.Builder()
                .setActivityType(DetectedActivity.IN_VEHICLE)
                .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
                .build()
  
        transitions +=
            ActivityTransition.Builder()
                .setActivityType(DetectedActivity.IN_VEHICLE)
                .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
                .build()
  
        transitions +=
            ActivityTransition.Builder()
                .setActivityType(DetectedActivity.WALKING)
                .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
                .build()
  
        transitions +=
            ActivityTransition.Builder()
                .setActivityType(DetectedActivity.WALKING)
                .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
                .build()
  
        transitions +=
            ActivityTransition.Builder()
                .setActivityType(DetectedActivity.STILL)
                .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
                .build()
  
        transitions +=
            ActivityTransition.Builder()
                .setActivityType(DetectedActivity.STILL)
                .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
                .build()
  
        transitions +=
            ActivityTransition.Builder()
                .setActivityType(DetectedActivity.RUNNING)
                .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
                .build()
  
        transitions +=
            ActivityTransition.Builder()
                .setActivityType(DetectedActivity.RUNNING)
                .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
                .build()
  
        return transitions
    }
  
    // pass the list of ActivityTransition objects to the ActivityTransitionRequest class
    fun getActivityTransitionRequest() = ActivityTransitionRequest(getTransitions())
  
    // ask to allow permissions
    @RequiresApi(Build.VERSION_CODES.Q)
    fun hasActivityTransitionPermissions(context: Context): Boolean =
        EasyPermissions.hasPermissions(
            context,
            Manifest.permission.ACTIVITY_RECOGNITION
        )
  
    // types of activities
    fun toActivityString(activity: Int): String {
        return when (activity) {
            DetectedActivity.STILL -> "STILL"
            DetectedActivity.WALKING -> "WALKING"
            DetectedActivity.IN_VEHICLE -> "IN VEHICLE"
            DetectedActivity.RUNNING -> "RUNNING"
            else -> "UNKNOWN"
        }
    }
  
    // type of transitions
    fun toTransitionType(transitionType: Int): String {
        return when (transitionType) {
            ActivityTransition.ACTIVITY_TRANSITION_ENTER -> "ENTER"
            ActivityTransition.ACTIVITY_TRANSITION_EXIT -> "EXIT"
            else -> "UNKNOWN"
        }
    }
}


Step 8: Create ActivityTransitionReceiver.kt class which receives the activities and notifies the user’s state 

Below is the complete code of the ActivityTransitionReceiver.kt class. Comments are added inside the code to understand each line of the code.

Kotlin




import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.widget.Toast
import com.google.android.gms.location.ActivityTransitionResult
import io.karn.notify.Notify
import java.text.SimpleDateFormat
import java.util.*
  
class ActivityTransitionReceiver : BroadcastReceiver() {
  
    override fun onReceive(context: Context, intent: Intent) {
        if (ActivityTransitionResult.hasResult(intent)) {
            val result = ActivityTransitionResult.extractResult(intent)
            result?.let {
                result.transitionEvents.forEach { event ->
                    // Info about activity
                    val info =
                        "Transition: " + ActivityTransitionsUtil.toActivityString(event.activityType) +
                                " (" + ActivityTransitionsUtil.toTransitionType(event.transitionType) + ")" + "   " +
                                SimpleDateFormat("HH:mm:ss", Locale.US).format(Date())
                    // notification details
                    Notify
                        .with(context)
                        .content {
                            title = "Activity Detected"
                            text = "I can see you are in ${
                                ActivityTransitionsUtil.toActivityString(
                                    event.activityType
                                )
                            } state"
                        }
                        .show(id = Constants.ACTIVITY_TRANSITION_NOTIFICATION_ID)
                    Toast.makeText(context, info, Toast.LENGTH_LONG).show()
                }
            }
        }
    }
}


Step 9: Working with MainActivity.kt.Below is the code for MainActivity.kt 

Below is the complete code for the MainActivity.kt file. Comments are added inside the code to understand each line of the code.

Kotlin




import android.Manifest
import android.app.PendingIntent
import android.content.Intent
import android.content.SharedPreferences
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import com.gfg.article.activityrecognition.Constants.ACTIVITY_TRANSITION_STORAGE
import com.google.android.gms.location.ActivityRecognition
import com.google.android.gms.location.ActivityRecognitionClient
import kotlinx.android.synthetic.main.activity_main.*
import pub.devrel.easypermissions.AppSettingsDialog
import pub.devrel.easypermissions.EasyPermissions
  
class MainActivity : AppCompatActivity(), EasyPermissions.PermissionCallbacks {
  
    lateinit var client: ActivityRecognitionClient
    lateinit var storage: SharedPreferences
  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
  
        // The Activity Recognition Client returns a 
          // list of activities that a user might be doing
        client = ActivityRecognition.getClient(this)
          
        // variable to check whether the user have already given the permissions
        storage = androidx.preference.PreferenceManager.getDefaultSharedPreferences(this)
  
        // check switch is on/off
        switchActivityTransition.isChecked = getSwitchState()
  
        switchActivityTransition.setOnCheckedChangeListener { _, isChecked ->
            if (isChecked) {
                // check for devices with Android 10 (29+).
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
                    // check for permission
                    && !ActivityTransitionsUtil.hasActivityTransitionPermissions(this)
                ) {
                    switchActivityTransition.isChecked = false
                    // request for permission
                    requestActivityTransitionPermission()
                } else {
                    // when permission is already allowed
                    requestForUpdates()
                }
            } else {
                saveSwitchState(false)
                deregisterForUpdates()
            }
        }
    }
  
    // when permission is denied
    @RequiresApi(Build.VERSION_CODES.Q)
    override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
        // permission is denied permanently
        if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
            AppSettingsDialog.Builder(this).build().show()
        } else {
            requestActivityTransitionPermission()
        }
    }
  
    // after giving permission
    override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
        switchActivityTransition.isChecked = true
        saveSwitchState(true)
        requestForUpdates()
    }
  
    // request for permission
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
    }
  
    // To register for changes we have to also supply the requestActivityTransitionUpdates() method
    // with the PendingIntent object that will contain an intent to the component
    // (i.e. IntentService, BroadcastReceiver etc.) that will receive and handle updates appropriately.
    private fun requestForUpdates() {
        client
            .requestActivityTransitionUpdates(
                ActivityTransitionsUtil.getActivityTransitionRequest(),
                getPendingIntent()
            )
            .addOnSuccessListener {
                showToast("successful registration")
            }
            .addOnFailureListener {
                showToast("Unsuccessful registration")
            }
    }
  
    // Deregistering from updates
    // call the removeActivityTransitionUpdates() method
    // of the ActivityRecognitionClient and pass 
    // ourPendingIntent object as a parameter
    private fun deregisterForUpdates() {
        client
            .removeActivityTransitionUpdates(getPendingIntent())
            .addOnSuccessListener {
                getPendingIntent().cancel()
                showToast("successful deregistration")
            }
            .addOnFailureListener { e: Exception ->
                showToast("unsuccessful deregistration")
            }
    }
  
    // creates and returns the PendingIntent object which holds 
    // an Intent to an BroadCastReceiver class
    private fun getPendingIntent(): PendingIntent {
        val intent = Intent(this, ActivityTransitionReceiver::class.java)
        return PendingIntent.getBroadcast(
            this,
            Constants.REQUEST_CODE_INTENT_ACTIVITY_TRANSITION,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT
        )
    }
  
    // requesting for permission
    @RequiresApi(Build.VERSION_CODES.Q)
    private fun requestActivityTransitionPermission() {
        EasyPermissions.requestPermissions(
            this,
            "You need to allow Activity Transition Permissions in order to recognize your activities",
            Constants.REQUEST_CODE_ACTIVITY_TRANSITION,
            Manifest.permission.ACTIVITY_RECOGNITION
        )
    }
  
    private fun showToast(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_LONG)
            .show()
    }
  
    // save switch state
    private fun saveSwitchState(value: Boolean) {
        storage
            .edit()
            .putBoolean(ACTIVITY_TRANSITION_STORAGE, value)
            .apply()
    }
  
    // get the state of switch
    private fun getSwitchState() = storage.getBoolean(ACTIVITY_TRANSITION_STORAGE, false)
}


Now, run the app

Output:

When user walks

When user have no movement

Source Code: Click Here



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads