Exception while trying to access Google Fit API - The user must be signed in to make this API call
Asked Answered
H

3

13

I am trying to set up a wearable app (for Huawei Watch 2) running on WearOS to provide a sort of continuous feed of Heart Rate (BPM) into a Google Fit account, which is read into another smartphone application.

The issue comes when I am trying to set up the account and access the data as it follows:

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

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.BODY_SENSORS)
        != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.BODY_SENSORS),GOOGLE_FIT_PERMISSIONS_REQUEST_CODE)
}

fitnessOptions = FitnessOptions.builder()
.addDataType(DataType.TYPE_HEART_RATE_BPM, FitnessOptions.ACCESS_READ)
.addDataType(DataType.TYPE_HEART_RATE_BPM, FitnessOptions.ACCESS_WRITE)
.build()

account = GoogleSignIn.getAccountForExtension(this, fitnessOptions)
if (!GoogleSignIn.hasPermissions(account, fitnessOptions)) {
    GoogleSignIn.requestPermissions(
        this, // your activity
        GOOGLE_FIT_PERMISSIONS_REQUEST_CODE, // e.g. 1
        account,
        fitnessOptions);
} else {
    accessGoogleFit()
}
timer.scheduleAtFixedRate(
    object : TimerTask() {
        override fun run() {
            Log.i("[TimerTask]", "Retrieving data..")
            accessGoogleFit()
            Log.i("[Account]", "" + account.email)

        }
    },0, 1000
)
    // Enables Always-on
setAmbientEnabled()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (resultCode) {
        Activity.RESULT_OK -> when (requestCode) {
            GOOGLE_FIT_PERMISSIONS_REQUEST_CODE -> accessGoogleFit()
            else -> {}
        }
        else -> {}
    }
}

private fun accessGoogleFit() {
    val cal: Calendar = Calendar.getInstance()
    val now = Date()
    cal.setTime(now)
    val endTime: Long = cal.getTimeInMillis()
    cal.add(Calendar.DAY_OF_MONTH, -1)
    val startTime: Long = cal.getTimeInMillis()

    val historyRequest = DataReadRequest.Builder()
    .read(DataType.TYPE_HEART_RATE_BPM)
    .enableServerQueries()
    .setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
    .build()
    val sourceRequest = DataSourcesRequest.Builder()
    .setDataTypes(DataType.TYPE_HEART_RATE_BPM)
    .setDataSourceTypes(DataSource.TYPE_RAW, DataSource.TYPE_DERIVED)
    .build()

    Fitness.getHistoryClient(this,account)
    .readData(historyRequest)
    .addOnSuccessListener{
        response-> txt_GoogleFit_FitData.setText(response.dataSets.get(0).toString())
    }
    .addOnFailureListener{ e ->
    Log.e("[GoogleFIT]", "Find data sources request failed", e)
}

Fitness.getSensorsClient(this, account)
.findDataSources(sourceRequest)
.addOnSuccessListener { dataSources ->
dataSources.forEach {
    Log.i("[GoogleFIT]", "Data source found: ${it.streamIdentifier}")
    Log.i("[GoogleFIT]", "Data Source type: ${it.dataType.name}")

    if (it.dataType == DataType.TYPE_HEART_RATE_BPM) {
        Log.i("[GoogleFIT]", "Data source for LOCATION_SAMPLE found!")
    }
}
}
.addOnFailureListener { e ->
Log.e("[GoogleFIT]", "Find data sources request failed", e)
}
}

Stack trace of exception:

2021-01-27 17:08:07.032 13743-13767/com.example.watch_bpmupdated2 I/[TimerTask]: Retrieving data..
2021-01-27 17:08:07.036 13743-13767/com.example.watch_bpmupdated2 I/[Account]: <<default account>>
2021-01-27 17:08:07.057 13743-13743/com.example.watch_bpmupdated2 E/[GoogleFIT]: Find data sources request failed
    com.google.android.gms.common.api.ApiException: 4: The user must be signed in to make this API call.
        at com.google.android.gms.common.internal.ApiExceptionUtil.fromStatus(com.google.android.gms:play-services-base@@17.1.0:4)
        at com.google.android.gms.common.internal.zai.zaf(com.google.android.gms:play-services-base@@17.1.0:2)
        at com.google.android.gms.common.internal.zak.onComplete(com.google.android.gms:play-services-base@@17.1.0:6)
        at com.google.android.gms.common.api.internal.BasePendingResult.zaa(com.google.android.gms:play-services-base@@17.1.0:176)
        at com.google.android.gms.common.api.internal.BasePendingResult.setResult(com.google.android.gms:play-services-base@@17.1.0:135)
        at com.google.android.gms.common.api.internal.BaseImplementation$ApiMethodImpl.setFailedResult(com.google.android.gms:play-services-base@@17.1.0:29)
        at com.google.android.gms.common.api.internal.zad.zaa(com.google.android.gms:play-services-base@@17.1.0:9)
        at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zac(com.google.android.gms:play-services-base@@17.1.0:175)
        at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.onConnectionFailed(com.google.android.gms:play-services-base@@17.1.0:79)
        at com.google.android.gms.common.internal.zag.onConnectionFailed(com.google.android.gms:play-services-base@@17.1.0:2)
        at com.google.android.gms.common.internal.BaseGmsClient$zzg.zza(com.google.android.gms:play-services-basement@@17.1.1:6)
        at com.google.android.gms.common.internal.BaseGmsClient$zza.zza(com.google.android.gms:play-services-basement@@17.1.1:25)
        at com.google.android.gms.common.internal.BaseGmsClient$zzb.zzo(com.google.android.gms:play-services-basement@@17.1.1:11)
        at com.google.android.gms.common.internal.BaseGmsClient$zzc.handleMessage(com.google.android.gms:play-services-basement@@17.1.1:49)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at com.google.android.gms.internal.common.zzi.dispatchMessage(com.google.android.gms:play-services-basement@@17.1.1:8)
        at android.os.Looper.loop(Looper.java:164)
        at android.os.HandlerThread.run(HandlerThread.java:65)
2021-01-27 17:08:07.060 13743-13743/com.example.watch_bpmupdated2 E/[GoogleFIT]: Find data sources request failed
    com.google.android.gms.common.api.ApiException: 4: The user must be signed in to make this API call.
        at com.google.android.gms.common.internal.ApiExceptionUtil.fromStatus(com.google.android.gms:play-services-base@@17.1.0:4)
        at com.google.android.gms.common.internal.zai.zaf(com.google.android.gms:play-services-base@@17.1.0:2)
        at com.google.android.gms.common.internal.zak.onComplete(com.google.android.gms:play-services-base@@17.1.0:6)
        at com.google.android.gms.common.api.internal.BasePendingResult.zaa(com.google.android.gms:play-services-base@@17.1.0:176)
        at com.google.android.gms.common.api.internal.BasePendingResult.setResult(com.google.android.gms:play-services-base@@17.1.0:135)
        at com.google.android.gms.common.api.internal.BaseImplementation$ApiMethodImpl.setFailedResult(com.google.android.gms:play-services-base@@17.1.0:29)
        at com.google.android.gms.common.api.internal.zad.zaa(com.google.android.gms:play-services-base@@17.1.0:9)
        at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zac(com.google.android.gms:play-services-base@@17.1.0:175)
        at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.onConnectionFailed(com.google.android.gms:play-services-base@@17.1.0:79)
        at com.google.android.gms.common.internal.zag.onConnectionFailed(com.google.android.gms:play-services-base@@17.1.0:2)
        at com.google.android.gms.common.internal.BaseGmsClient$zzg.zza(com.google.android.gms:play-services-basement@@17.1.1:6)
        at com.google.android.gms.common.internal.BaseGmsClient$zza.zza(com.google.android.gms:play-services-basement@@17.1.1:25)
        at com.google.android.gms.common.internal.BaseGmsClient$zzb.zzo(com.google.android.gms:play-services-basement@@17.1.1:11)
        at com.google.android.gms.common.internal.BaseGmsClient$zzc.handleMessage(com.google.android.gms:play-services-basement@@17.1.1:49)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at com.google.android.gms.internal.common.zzi.dispatchMessage(com.google.android.gms:play-services-basement@@17.1.1:8)
        at android.os.Looper.loop(Looper.java:164)
        at android.os.HandlerThread.run(HandlerThread.java:65)

My watch is paired to the smartphone I am using to read the data (I just need faster updates from google fit, and thought about forcing this. Code not available here for that). The same google fit account is present, logged into Google Fit app and synced on both devices. Everything runs smoothly on the smartphone (even with the same code), while on the Watch I get the "user must be signed in" exception when setting up the account.

Google services dependencies, application permissions are set the same way on both devices, as well as the required OAuth settings, SHA1 and credentials on the developer console.

Could this be an issue related to the watch->App->Google Fit account synchronization, or am I missing something?

Hearty answered 27/1, 2021 at 15:14 Comment(6)
Any luck on solving this? I'm stuck on the exact same issue!Venereal
Sadly, haven`t found a fix yet.Hearty
I am stuck on this tooComprehend
Same problem. I wonder this is related to this ? developers.google.com/terms/api-services-user-data-policyMorrell
Also trying to communicate with Google Fit and after successfully doing it for less than a day it won't work anymoreDirndl
You should set up your project in the Google Console and add it to the test enviroment. Only accounts that are added as testers have access to it. Does this help?Galleon
G
2

Register the project in the Google Console and add your Google Account as a test user after adding the OAuth 2.0 Client ID for your project.

This should fix the login issue.

Galleon answered 15/12, 2021 at 16:24 Comment(9)
I already did this, and it worked for around 1 day before stopping wrkingDirndl
Do you receive the same error message? Or what is the error message? Did you change Google Accounts on accident?Galleon
Nope, I'm testing on my main phone so my account is still the same , the error is also mostly the same with just underlying path slightly changing gist.github.com/Aviortheking/13f50d7062181bbcf58a734f48475d0aDirndl
I had the same issue or similar issue. Does your OAuth consent screen look like this for example? imgur.com/Y9UMJik The Google Account should be inserted where I blacked out my e-mail. Did you re-install Android Studio or deinstalled it? Reinstalling/deinstalling resets the SHA1 fingerprint. After what I did in the picture the issue was solved. I also answered this on GitHub somewhere. In case this works please accept the answer or upvote.Galleon
When I added my email in and it worked for some time like I said for around 20 hours the first time. I checked my SHA1 and it didn't changed, I also tried removing/re-adding my test account back and no changeDirndl
I really do not know then. Is your wifi set off from the phone? This was the last issue I ran into. It should work. Or the wrong Project is chosen or the Fitness API disabled?Galleon
Tested with WiFi On/Off, Correct project chosen (verified twice) and Fitness API enabled :(Dirndl
Do you resolve the prob, i have the same :( The prob when i user TYPE_STEP_COUNT_DELTA it worked fineMin
I posted an answer that should fix your problemDirndl
D
1

Here is how I fixed it:

  • Verify that you have a test user in Google Cloud with your Email
  • Verify that you have a OAuth 2.0 Client IDs on Google Cloud Credentials linked with your certificate
  • Verify that your app certificate on Android using an app like Package Manager correspond to the written above
  • Make sure that you call GoogleSignIn.hasPermissions before trying to access anything else and that it returns true

Theses checked should fix most errors from Google Fit not giving you any results or keeping you on the loading screen

Also, Google Fit historical requests need to have a good TimeUnit ex for Type_WEIGHT TimeUnit should be MILLISECONDS as any higher will not return any values

ex historical request builder:

DataReadRequest.Builder()
    .read(DataType.TYPE_WEIGHT)
    .setTimeRange(/* 1st January 2013 */, /* now */, TimeUnut.MILLISECONDS)
    .build()
Dirndl answered 3/1, 2022 at 16:38 Comment(0)
C
1

FIXED

I've figured out what is the main issue here. The dialog that appears to your, that allows you to actually select your Google account has a bug. Apparently when you do select an account from it, your account is not actually selected, because somehow, dialog disappears before it's selected. To confirm that, just remove your app from the background and run it again. You will see that same dialog appearing once again, which means you haven't selected your account previously.

To fix that, just be sure to hold down your finger on the Google account from that dialog, and release it after a second or two, to make sure it's actually selected. Otherwise if you're account is not selected, then that dialog will appear again whenever you close and re-run the app, asking you to choose your account all over again.

Or just add the logic to trigger that dialog once again, if you receive this "The user must be signed in to make this API call" error.


If none of the above doesn't work, try adding scopes in the OAuth consent screen. In my case, I was building an app to read/write the data about steps. Which is why I enabled those two scopes:

.../auth/fitness.activity.write
.../auth/fitness.activity.read
Cooper answered 22/2, 2023 at 15:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.