Android 中的活动识别
在本文中,我们将学习一个名为Activity Recognition Transition API或Transition API的 API,它旨在检测特定用户的活动,例如驾驶、步行或跑步等。 有许多应用程序使用此 Activity识别转换 API。例如,当您开始驾驶汽车或自行车时,公里查找器应用程序开始运行,并在您停止驾驶时停止。此活动识别的其他示例可以是任何健康和健身应用程序,它确定您在特定日期跑步或步行的米数或公里数,之后,您可以找到当天燃烧的卡路里等等。
在这里,我们将制作一个应用程序来检测用户的活动,如静止、跑步、步行、驾驶或其他活动。下面给出了一个示例屏幕截图,以了解我们将在本文中做什么。
分步实施
第 1 步:创建一个新项目
要在 Android Studio 中创建新项目,请参阅如何在 Android Studio 中创建/启动新项目。请注意,选择Kotlin作为编程语言。
第二步:添加依赖
// 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’
第三步:在顶级build.gradle中添加对maven仓库的引用
allprojects {
repositories {
…
// For Notify library
maven { url ‘https://jitpack.io’ }
}
}
第 4 步:使用 AndroidMenifest.xml
确保添加以下权限
….
….
android:name=”.ActivityTransitionReceiver” android:exported=”false” android:permission=”com.google.android.gms.permission.ACTIVITY_RECOGNITION”>
第 5 步:添加应用程序的 UI。
导航到app > res > layout > activity_main.xml并将以下代码添加到该文件中。下面是activity_main.xml文件的代码。
XML
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"
}
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 {
// List of activity transitions to track
val transitions = mutableListOf()
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"
}
}
}
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()
}
}
}
}
}
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) {
// 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) {
switchActivityTransition.isChecked = true
saveSwitchState(true)
requestForUpdates()
}
// request for permission
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array,
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)
}
第 6 步:创建 Constant.kt 类并在此处写入所有必需的常量
科特林
// 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"
}
在 Android 中,我们有Activity Recognition Client ,它会定期唤醒您的设备,然后从小工具的传感器收集信息,然后在某些机器学习计算的帮助下,利用收集到的信息来决定活动。您应该简单地使用 Activity Recognition Client,API 将为您提供学习 ML 的没有令人信服的理由。
以下是活动识别客户端可以检测到的活动:
- STILL:当用户坐在某个地方或移动设备没有运动时移动设备将静止时,活动识别客户端将检测到 STILL 活动。
- ON_FOOT:当移动设备以正常速度移动时,即携带移动设备的用户正在步行或跑步时,活动识别客户端将检测到 ON_FOOT 活动。
- WALKING:这是携带移动设备的用户行走时,由Activity Recognition Client检测到的ON_FOOT活动的一个子活动。
- RUNNING:这也是ON_FOOT活动的一个子活动,当用户携带移动设备正在运行时,由活动识别客户端检测到。
- IN_VEHICLE:当移动设备在公共汽车或汽车或某种其他类型的车辆中或持有移动设备的用户在车辆中时,会检测到此活动。
- ON_BICYCLE:当设备在自行车上或携带手机的用户在自行车上时,将检测到此活动。
- 倾斜:当移动设备被抬起并与平面成一定角度时,活动识别客户端将检测到此活动。
- 未知:当设备无法检测到移动设备上的任何活动时,活动识别客户端将显示此结果。
第 7 步:创建 ActivityTransitionsUtil.kt 类,该类将包含活动类型和转换类型的列表
要开始接收有关活动转换的通知,我们必须构建一个 ActivityTransitionRequest 对象来确定活动和转换的类型。为此,我们必须创建一个 ActivityTransition 对象列表并将其传递给 ActivityTransitionRequest 类。下面是 ActivityTransitionsUtil.kt 类的完整代码。代码中添加了注释以了解代码的每一行。
科特林
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 {
// List of activity transitions to track
val transitions = mutableListOf()
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"
}
}
}
第 8 步:创建 ActivityTransitionReceiver.kt 类,该类接收活动并通知用户状态
下面是 ActivityTransitionReceiver.kt 类的完整代码。代码中添加了注释以了解代码的每一行。
科特林
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()
}
}
}
}
}
第 9 步:使用 MainActivity.kt。下面是 MainActivity.kt 的代码
下面是MainActivity.kt文件的完整代码。代码中添加了注释以了解代码的每一行。
科特林
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) {
// 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) {
switchActivityTransition.isChecked = true
saveSwitchState(true)
requestForUpdates()
}
// request for permission
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array,
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)
}
现在,运行应用程序
输出:
源代码:点击这里