Human Activity Recognition In Android

This article explains you how to detect human activities like walking, running, driving etc. using android application. We are using Activity Recognition Client api to detect human activities.

Activity Recognition Client works with sensors of android device to collect data of different human activities. In this article, We can also track data when app is in background with the help of a background service.

 

Activities Types

  1. STILL:  This activity is recognized when android device has no movement.
  2. ON_FOOT:  This activity is recognized when the android device is moving at a normal speed.
  3. WALKING:  This activity is recognized when the user carrying the android device while walking.
  4. RUNNING: This activity is recognized when the user carrying the android device while running.
  5. IN_VEHICLE: This activity is recognized when the android device is in the car or some other vehicles.
  6. ON_BICYCLE: This activity is recognized when the user carrying the android device while riding on the bicycle.
  7. TILTING: This activity is recognized when the android device is being lifted and is having some angle with the flat surface.
  8. UNKNOWN: This activity is recognized when the android device sensor is unable to detect any activity.
 
 

Add below dependency in app level build.gradle

implementation ‘com.google.android.gms:play-services-location:17.0.0’
implementation ‘androidx.localbroadcastmanager:localbroadcastmanager:1.0.0’
 
 

Add Required Permissions

<!– Required for api version 28 and below –>
<uses-permission android:name=”com.google.android.gms.permission.ACTIVITY_RECOGNITION” />
<!– Required for api version 29 and above –>
<uses-permission android:name=”android.permission.ACTIVITY_RECOGNITION” />
 
 

Creating an IntentService

We are creating a class which extends IntentService. In this class we can receive activity type and confidence. Confidence parameter gives intensity of activity so we can confirm human activity. If confidence percentage is greater than 70 then we can sure activity is done by user.

class RecognizedActivitiesService : IntentService(TAG) {

companion object {
    private val TAG = RecognizedActivitiesService::class.java.name
}

override fun onHandleIntent(receivedIntent: Intent?) {

    val recognitionResult = ActivityRecognitionResult.extractResult(receivedIntent)

    val recognizedActivities = recognitionResult.probableActivities as ArrayList<*>

    for (activity in recognizedActivities) {
        val intent = Intent(MainActivity.BROADCAST_ACTIVITY_RECOGNITION)
        intent.putExtra(“type”, (activity as DetectedActivity).type)
        intent.putExtra(“confidence”, (activity as DetectedActivity).confidence)
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
    }
}

}

 
 

Start Activity Recognition in Background Service

We are using background service to detect human activities so we can also recognize human activity even if app is in the background.

private var mIntent: Intent? = null
private var pendingIntent: PendingIntent? = null
private var activityRecognitionClient: ActivityRecognitionClient? = null

activityRecognitionClient = ActivityRecognitionClient(this)
mIntent = Intent(this, RecognizedActivitiesService::class.java)

mIntent?.let {
    pendingIntent = PendingIntent.getService( this, 1, mIntent!!, PendingIntent.FLAG_UPDATE_CURRENT)
    startActivityUpdates()
}

private fun startActivityUpdates() {
    val activityUpdates = activityRecognitionClient?.requestActivityUpdates(
    MainActivity.DETECTION_INTERVAL_IN_MILLISECONDS, pendingIntent )

    activityUpdates?.addOnSuccessListener {
        Log.e(TAG, “Start Activity Updates Success”)
    }

    activityUpdates?.addOnFailureListener {
        Log.e(TAG, “Start Activity Updates Fail”)
    }
}

 
 

Stop Activity Recognition When app is closing

override fun onDestroy() {
    super.onDestroy()
    stopActivityUpdates()
}

fun stopActivityUpdates() {
    val activityUpdates = activityRecognitionClient?.removeActivityUpdates( pendingIntent )

    activityUpdates?.addOnSuccessListener {
        Log.e(TAG, “Stop Activity Updates Success”)
    }

    activityUpdates?.addOnFailureListener {
        Log.e(TAG, “Stop Activity Updates Fail”)
    }
}

 
 

Create Broadcast Receiver to handle Activity Recognition

private var broadcastReceiver: BroadcastReceiver? = null

broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
    if (intent.action == MainActivity.BROADCAST_ACTIVITY_RECOGNITION) {
        val type = intent.getIntExtra(“type”, -1)
        val confidence = intent.getIntExtra(“confidence”, 0)
        handleUserActivity(type, confidence)
    }
}
}

override fun onResume() {
    super.onResume()
    broadcastReceiver?.let {
        LocalBroadcastManager.getInstance(this).registerReceiver(
            it, IntentFilter(BROADCAST_ACTIVITY_RECOGNITION) )
    }
}

override fun onPause() {
    super.onPause()
    broadcastReceiver?.let{
        LocalBroadcastManager.getInstance(this).unregisterReceiver(it)
    }
}

 
 

Display Activity Type and Confidence

var activityType = “Activity Unknown”

when (type) {
    DetectedActivity.STILL -> {
        activityType = “Still”
    }
    DetectedActivity.ON_FOOT -> {
        activityType = “On Foot”
    }
    DetectedActivity.WALKING -> {
        activityType = “Walking”
    }
    DetectedActivity.RUNNING -> {
        activityType = “Running”
    }
    DetectedActivity.IN_VEHICLE -> {
        activityType = “In Vehicle”
    }
    DetectedActivity.ON_BICYCLE -> {
        activityType = “On Bicycle”
    }
    DetectedActivity.TILTING -> {
        activityType = “Tilting”
    }
    DetectedActivity.UNKNOWN -> {
        activityType = “Unknown Activity”
    }
}

if (confidence > 70) {
    txt_type?.text = activityType
    txt_confidence?.text = “Confidence: ” + confidence
}

 
 

Final Code

 

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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/txt_type"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="20dp"
        android:textAllCaps="true"
        android:textColor="@android:color/holo_orange_dark"
        android:textSize="30sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toTopOf="@+id/txt_confidence"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/txt_confidence"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:textAllCaps="true"
        android:textSize="18sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
 

RecognizedActivitiesService.kt

class RecognizedActivitiesService : IntentService(TAG) {

    companion object {
        private val TAG = RecognizedActivitiesService::class.java.name
    }

    override fun onHandleIntent(receivedIntent: Intent?) {

        val recognitionResult = ActivityRecognitionResult.extractResult(receivedIntent)

        val recognizedActivities = recognitionResult.probableActivities as ArrayList<*>

        for (activity in recognizedActivities) {
            val intent = Intent(MainActivity.BROADCAST_ACTIVITY_RECOGNITION)
            intent.putExtra("type", (activity as DetectedActivity).type)
            intent.putExtra("confidence", (activity as DetectedActivity).confidence)
            LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
        }
    }

}
 

BackgroundRecognizedActivitiesService.kt

class BackgroundRecognizedActivitiesService : Service() {
    private val TAG = BackgroundRecognizedActivitiesService::class.java.name
    private var mIntent: Intent? = null
    private var pendingIntent: PendingIntent? = null
    private var activityRecognitionClient: ActivityRecognitionClient? = null
    internal var mBinder: IBinder = LocalBinder()

    inner class LocalBinder : Binder() {
        val service: BackgroundRecognizedActivitiesService
            get() = this@BackgroundRecognizedActivitiesService
    }

    override fun onCreate() {
        super.onCreate()
        activityRecognitionClient = ActivityRecognitionClient(this)
        mIntent = Intent(this, RecognizedActivitiesService::class.java)
        mIntent?.let {
            pendingIntent =
                PendingIntent.getService(this, 1, mIntent!!, PendingIntent.FLAG_UPDATE_CURRENT)
            startActivityUpdates()
        }
    }

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

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)
        return START_STICKY
    }

    private fun startActivityUpdates() {
        val activityUpdates = activityRecognitionClient?.requestActivityUpdates(
            MainActivity.DETECTION_INTERVAL_IN_MILLISECONDS,
            pendingIntent
        )

        activityUpdates?.addOnSuccessListener {
            Log.e(TAG, "Start Activity Updates Success")
        }

        activityUpdates?.addOnFailureListener {
            Log.e(TAG, "Start Activity Updates Fail")
        }
    }

    fun stopActivityUpdates() {
        val activityUpdates = activityRecognitionClient?.removeActivityUpdates(
            pendingIntent
        )

        activityUpdates?.addOnSuccessListener {
            Log.e(TAG, "Stop Activity Updates Success")
        }

        activityUpdates?.addOnFailureListener {
            Log.e(TAG, "Stop Activity Updates Fail")
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        stopActivityUpdates()
    }

}
 

MainActivity.kt

class MainActivity : AppCompatActivity() {

    private var broadcastReceiver: BroadcastReceiver? = null

    companion object {
        val BROADCAST_ACTIVITY_RECOGNITION = "activity_recognition"
        internal val DETECTION_INTERVAL_IN_MILLISECONDS: Long = 1000
    }

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

        broadcastReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                if (intent.action == MainActivity.BROADCAST_ACTIVITY_RECOGNITION) {
                    val type = intent.getIntExtra("type", -1)
                    val confidence = intent.getIntExtra("confidence", 0)
                    handleUserActivity(type, confidence)
                }
            }
        }

        val intent = Intent(this@MainActivity, BackgroundRecognizedActivitiesService::class.java)
        startService(intent)
    }

    private fun handleUserActivity(type: Int, confidence: Int) {
        var activityType = "Activity Unknown"

        when (type) {
            DetectedActivity.STILL -> {
                activityType = "Still"
            }
            DetectedActivity.ON_FOOT -> {
                activityType = "On Foot"
            }
            DetectedActivity.WALKING -> {
                activityType = "Walking"
            }
            DetectedActivity.RUNNING -> {
                activityType = "Running"
            }
            DetectedActivity.IN_VEHICLE -> {
                activityType = "In Vehicle"
            }
            DetectedActivity.ON_BICYCLE -> {
                activityType = "On Bicycle"
            }
            DetectedActivity.TILTING -> {
                activityType = "Tilting"
            }
            DetectedActivity.UNKNOWN -> {
                activityType = "Unknown Activity"
            }
        }

        Log.e("Activity Type:", activityType)
        Log.e("Activity Confidence:", confidence.toString())

        if (confidence > 70) {
            txt_type?.text = activityType
            txt_confidence?.text = "Confidence: " + confidence
        }
    }

    override fun onResume() {
        super.onResume()
        broadcastReceiver?.let {
            LocalBroadcastManager.getInstance(this).registerReceiver(
                it,
                IntentFilter(BROADCAST_ACTIVITY_RECOGNITION)
            )
        }
    }

    override fun onPause() {
        super.onPause()
        broadcastReceiver?.let{
            LocalBroadcastManager.getInstance(this).unregisterReceiver(it)
        }
    }
}
 
 
Activity Recognition
 
 
 

Leave a Reply