android / health-samples

Apache License 2.0
271 stars 150 forks source link

Collecting UserActivityInfo not working #84

Open borismedved0 opened 1 year ago

borismedved0 commented 1 year ago

Hello, I have encountered an issue: I am not getting new user activity info in fun onUserActivityInfoReceived(info: UserActivityInfo). To say that I do not receive at all is not entirely correct, I receive, but I receive this data immediately after I registered the service with setPassiveListenerServiceAsync and this data is outdated. Here's an log of UserActivityInfo I collected for a day:

write time to DB userActivityInfo.stateChangeTime.toMillis activity state
1 1678282350577 - 08 2023 13:32:30 1678177045675 - 07 2023 08:17:25 passive
2 1678263022160 - 08 2023 08:10:22 - end of sleep 1678177045669 - 07 2023 08:17:25 passive
3 1678223467842 - 07 2023 21:11:07 - start of sleep 1678177045676 - 07 2023 08:17:25 passive
4 1678223370897 - 07 2023 21:09:30 1678177045670 - 07 2023 08:17:25 passive
5 1678209916454 - 07 2023 17:25:16 1678177045676 - 07 2023 08:17:25 passive
6 1678208627154 - 07 2023 17:03:47 1678177045676 - 07 2023 08:17:25 passive
7 1678206676803 - 07 2023 16:31:16 1678177045676 - 07 2023 08:17:25 passive
8 1678205537674 - 07 2023 16:12:17 1678177045676 - 07 2023 08:17:25 passive
9 1678204862883 - 07 2023 16:01:02 1678177045676 - 07 2023 08:17:25 passive
10 1678204832426 - 07 2023 16:00:32 1678177045676 - 07 2023 08:17:25 passive
11 1678204827020 - 07 2023 16:00:27 1678177045676 - 07 2023 08:17:25 passive
12 1678204820542 - 07 2023 16:00:20 1678177045676 - 07 2023 08:17:25 passive
13 1678184515495 - 07 2023 10:21:55 1678177045676 - 07 2023 08:17:25 passive
14 1678179278383 - 07 2023 08:54:38 1678177045676 - 07 2023 08:17:25 passive
15 1678178784282 - 07 2023 08:46:24 1678177045676 - 07 2023 08:17:25 passive
16 1678177767542 - 07 2023 08:29:27 1678177045676 - 07 2023 08:17:25 passive
17 1678177675343 - 07 2023 08:27:55 1678177045676 - 07 2023 08:17:25 passive

All the data here, these are the ones that I received immediately after registering the service. If you say that you shouldn’t register so often, then it’s fine, I have an interval in 11 hours when I didn't registered service, it's an interval 2 and 3, there is a period of time when I slept with the watch, and in theory, between these records, there should be other activities that say that I’m asleep, but as you can see there's nothing. And that's the problem, I want to track my sleep duration but I can't because I'm not getting activity changes :(

Code

Manifest.sml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-feature android:name="android.hardware.type.watch" />

    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <!-- For receiving heart rate data. -->
    <uses-permission android:name="android.permission.BODY_SENSORS" />
    <!-- For receiving steps data. -->
    <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
    <!-- Needed in order to re-register for data on device startup. -->
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <!-- Probably need for tracking activity, TODO: remove if it's not necessary -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <!-- Probably need for tracking activity, TODO: remove if it's not necessary -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

    <!-- When an app targets Android 11 (API level 30) or higher and queries for information about
         the other apps that are installed on a device, the system filters this information by
         default. Adding the query field allows Health Services to recognize the app.
         See https://developer.android.com/training/package-visibility. -->
    <queries>
        <package android:name="com.google.android.wearable.healthservices" />
    </queries>

    <queries>
        <package android:name="com.google.android.apps.healthdata" />
    </queries>

    <application
        android:name=".presentation.App"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@android:style/Theme.DeviceDefault">
        <uses-library
            android:name="com.google.android.wearable"
            android:required="true" />

        <meta-data
            android:name="com.google.android.wearable.standalone"
            android:value="true" />

        <activity
            android:name=".presentation.android.activity.MainActivity"
            android:exported="true"
            android:launchMode="singleInstance"
            android:theme="@android:style/Theme.DeviceDefault">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>

        <!-- Complication -->
        <service
            android:name=".presentation.complication.PowerScoreComplicationProviderService"
            android:icon="@drawable/ic_power_score_4"
            android:label="@string/power_score"
            android:permission="com.google.android.wearable.permission.BIND_COMPLICATION_PROVIDER"
            android:exported="true">
            <intent-filter>
                <action android:name="android.support.wearable.complications.ACTION_COMPLICATION_UPDATE_REQUEST" />
            </intent-filter>

            <meta-data
                android:name="android.support.wearable.complications.SUPPORTED_TYPES"
                android:value="SMALL_IMAGE" />
            <meta-data
                android:name="android.support.wearable.complications.UPDATE_PERIOD_SECONDS"
                android:value="100" />
        </service>

        <service
            android:name=".presentation.android.healthservices.services.PassiveDataService"
            android:exported="true"
            android:permission="com.google.android.wearable.healthservices.permission.PASSIVE_DATA_BINDING" />

        <!-- Receiver that re-registers for background data after a device restart. -->
        <receiver
            android:name=".presentation.android.receiver.StartupReceiver"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
        <!-- Receiver that re-registers for background data with some interval. -->
        <receiver android:name=".presentation.android.receiver.RepeatedRegistrarReceiver" />
        <!-- Receiver that helps with scheduled notifications. -->
        <receiver android:name=".presentation.android.receiver.NotificationReceiver" />
        <!-- Receiver that helps with syncing next pvt time. -->
        <receiver android:name=".presentation.android.receiver.NextPvtTimeSyncReceiver" />
        <!-- Receiver that helps with syncing power gauge sync. -->
        <receiver android:name=".presentation.android.receiver.PowerGaugeSyncReceiver" />

        <!-- Our application implements Configuration.Provider, so we don't need this to initialize WorkManager. -->
        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">
            <!-- If you are using androidx.startup to initialize other components -->
            <meta-data
                android:name="androidx.work.WorkManagerInitializer"
                android:value="androidx.startup"
                tools:node="remove" />
        </provider>
    </application>
</manifest>

Registered service here

    suspend fun registerListenerForFitData(dataTypes: Set<DataType<*, *>>) {
        val passiveListenerConfig = PassiveListenerConfig.builder()
            .setShouldUserActivityInfoBeRequested(shouldUserActivityInfoBeRequested = true)
            .setDataTypes(dataTypes)
            .build()

        passiveMonitoringClient.setPassiveListenerServiceAsync(
            PassiveDataService::class.java,
            passiveListenerConfig
        ).await()
    }

PassiveDataService.kt

class PassiveDataService : PassiveListenerService() {

    private val activityStateManager: ActivityStateManager by inject()
    private val job = SupervisorJob()
    private val scope = CoroutineScope(Dispatchers.IO + job)

    override fun onUserActivityInfoReceived(info: UserActivityInfo) {
        scope.launch {
            activityStateManager.addActivityState(
                activityName = info.userActivityState.toActivityName(),
                timeInMillis = info.stateChangeTime.toEpochMilli()
            )
            Log.d("TAG!", "userActivityState: ${info.userActivityState.name}")
        }
    }

    override fun onNewDataPointsReceived(dataPoints: DataPointContainer) {
    ...
    }
}

Environments:

Device: Fossil Gen 6 OS: Wear OS 3 Android SDK: 30 Lib version: 1.0.0-beta02

Notes:

Thank you in advance!

Belozerow commented 1 year ago

I have the same issue

I've added logs to onCreate/onDestroy. Service starts but onUserActivityInfoReceived is not called

It was working fine as a Receiver in 1.0.0-alpha03, but in RC and in beta3 as a service it doesn't work

ashishpat commented 1 year ago

Having the same issues, using 'setPassiveListenerService' instead of setPassiveListenerServiceAsync worked for me

Belozerow commented 1 year ago

@ashishpat

Having the same issues, using 'setPassiveListenerService' instead of setPassiveListenerServiceAsync worked for me

setPassiveListenerService calls the same setPassiveListenerServiceAsync inside

@kotlin.jvm.Throws(HealthServicesException::class)
public suspend fun PassiveMonitoringClient.setPassiveListenerService(
    service: Class<out PassiveListenerService>,
    config: PassiveListenerConfig
) = setPassiveListenerServiceAsync(service, config).awaitWithException()