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
- STILL: This activity is recognized when android device has no movement.
- ON_FOOT: This activity is recognized when the android device is moving at a normal speed.
- WALKING: This activity is recognized when the user carrying the android device while walking.
- RUNNING: This activity is recognized when the user carrying the android device while running.
- IN_VEHICLE: This activity is recognized when the android device is in the car or some other vehicles.
- ON_BICYCLE: This activity is recognized when the user carrying the android device while riding on the bicycle.
- TILTING: This activity is recognized when the android device is being lifted and is having some angle with the flat surface.
- 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 ‘androidx.localbroadcastmanager:localbroadcastmanager:1.0.0’
Add Required Permissions
<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.
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 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
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
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
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) } } }

