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

Viewed 1386

I am trying to set up a wearable app (for Huawei Watch 2) running on WearOS to provide a sort of continous feed of Heart Rate (BPM) into a Google Fit account, which is read into another smarphone 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?

2 Answers

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.

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()
Related