I'm new to android development. Now, I'm trying to develop an app for detecting user activities. In this case, I used Google Play Services Activity Recognition API, and also, I followed the instructions (e.g., the google sample code for the API).
The app runs successfully and correctly with full functionality (on the app side), also I'm registered for the API successfully using the requestActivityTransitionUpdates().
The problem is the API doesn't update the user transition events and the ActivityTransitionResult.extractResult(intent).transitionEvents() is always empty. I don't have any idea how to solve this problem because I have this problem also with the Sleep API, too.
HELP!
Receiver.kt
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.example.hendi.util.ActivityTransitionUtil
import com.google.android.gms.location.ActivityTransitionResult
class ActivityTransitionReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("MyShedCheck", "onReceive()")
if (ActivityTransitionResult.hasResult(intent)) {
val result = ActivityTransitionResult.extractResult(intent)
result?.let {
result.transitionEvents.forEach { event ->
Log.d("RECEIVER", "Activity Detected:" +
"\ncontext: ${context}" +
"\nintent:${intent}" +
"\ntype: ${ActivityTransitionUtil.toActivityString(event.activityType)}"
)
}
}
}
}
}
MainActivity.kt
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.Intent
import android.content.SharedPreferences
import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.preference.PreferenceManager
import com.example.hendi.receiver.ActivityTransitionReceiver
import com.example.hendi.ui.theme.HendiTheme
import com.example.hendi.util.ActivityTransitionUtil
import com.example.hendi.util.Constants
import com.example.hendi.util.PermissionState
import com.google.android.gms.location.ActivityRecognition
import com.google.android.gms.location.ActivityRecognitionClient
class MainActivity : ComponentActivity() {
lateinit var client: ActivityRecognitionClient
lateinit var storage: SharedPreferences
companion object {
const val TAG = "MyShedCheck"
}
@RequiresApi(Build.VERSION_CODES.S)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
client = ActivityRecognition.getClient(this)
storage = PreferenceManager.getDefaultSharedPreferences(this)
var isChecked = false
val permissionState = PermissionState(this) {
if (it.permissionGranted) {
isChecked = true
requestForUpdates()
} else {
isChecked = false
it.requestPermission()
}
}
setContent {
HendiTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
ToggleAction(permissionState = permissionState,
request = { requestForUpdates() },
remove = { deregisterForUpdates() })
}
}
}
}
@RequiresApi(Build.VERSION_CODES.S)
@SuppressLint("MissingPermission")
fun requestForUpdates() {
client
.requestActivityTransitionUpdates(
ActivityTransitionUtil.getActivityTransitionRequest(),
getPendingIntent()
)
.addOnSuccessListener {
Log.d(TAG, "Successful registration")
}
.addOnFailureListener {
Log.d(TAG,"Unsuccessful registration:$it")
}
}
@SuppressLint("MissingPermission")
@RequiresApi(Build.VERSION_CODES.S)
fun deregisterForUpdates() {
client
.removeActivityTransitionUpdates(getPendingIntent())
.addOnSuccessListener {
Log.d(TAG, "Successful deregistration")
}
.addOnFailureListener {
Log.d(TAG,"Unsuccessful deregistration:$it")
}
}
@RequiresApi(Build.VERSION_CODES.S)
private fun getPendingIntent() :PendingIntent {
val intent = Intent(this, ActivityTransitionReceiver::class.java)
return PendingIntent.getBroadcast(
this,
122,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
}
}
@Composable
fun ToggleAction(permissionState: PermissionState, request: () -> Unit, remove: () -> Unit) {
Log.d("MyShedCheck", "Permission: ${permissionState.permissionGranted}")
var isChecked by remember { mutableStateOf(
if (permissionState.permissionGranted) {
true
} else {
permissionState.requestPermission()
false
}
)
}
Switch(checked = isChecked,
onCheckedChange = {
isChecked = it
if (it) {
request()
} else {
remove()
}
}
)
}
AndroidManifast.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- 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
android:name=".App"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Tracker"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Tracker">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".receiver.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>
</manifest>
ActivityTransitionUtil.kt
import android.content.Context
import com.google.android.gms.location.ActivityTransition
import com.google.android.gms.location.ActivityTransitionRequest
import com.google.android.gms.location.DetectedActivity
object ActivityTransitionUtil {
private fun getTransitions(): MutableList<ActivityTransition> {
val transitions = mutableListOf<ActivityTransition>()
transitions.add(
ActivityTransition.Builder()
.setActivityType(DetectedActivity.WALKING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build()
)
transitions.add(
ActivityTransition.Builder()
.setActivityType(DetectedActivity.WALKING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build()
)
transitions.add(
ActivityTransition.Builder()
.setActivityType(DetectedActivity.STILL)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build()
)
transitions.add(
ActivityTransition.Builder()
.setActivityType(DetectedActivity.STILL)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build()
)
return transitions
}
fun getActivityTransitionRequest() = ActivityTransitionRequest(getTransitions())
fun hasActivityTransitionPermissions(context: Context): Boolean =
context.hasPermission()
fun toActivityString(activity: Int): String {
return when (activity) {
DetectedActivity.STILL -> "STILL"
DetectedActivity.WALKING -> "WAKING"
else -> "UNKNOWN"
}
}
fun toTransitionType(transitionType: Int): String {
return when (transitionType) {
ActivityTransition.ACTIVITY_TRANSITION_ENTER -> "ENTER"
ActivityTransition.ACTIVITY_TRANSITION_EXIT -> "EXIT"
else -> "UNKNOWN"
}
}
}
App.kt
import android.app.Application
class App : Application() {
override fun onCreate() {
super.onCreate()
}
}