cph-cachet / flutter-plugins

A collection of Flutter plugins developed by CACHET
537 stars 642 forks source link

[Health2.0.9] Can't load step count from GoogleFit #243

Closed nongtiny closed 3 years ago

nongtiny commented 3 years ago

Device / Emulator and OS

NB: Bugs pertaining to old devices/OS versions will likely not be fixed.

Describe the bug

I copied the full example code. It works fine in iOS (Iphone11), but it does not work fine in Android.

To Reproduce

Copy the example code in your docs and test with the Android device.

Expected behavior

The app should return the step result list.

Actual behavior

Health Plugin Error:
I/flutter ( 2456):  NoSuchMethodError: The method 'map' was called on null.
I/flutter ( 2456): Receiver: null
I/flutter ( 2456): Tried calling: map<HealthDataPoint>(Closure: (dynamic) => HealthDataPoint)

Flutter doctor

[✓] Flutter (Channel stable, 1.22.3, on Mac OS X 10.15.7 19H2, locale en-TH)

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 12.1)
[!] Android Studio (version 4.1)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[✓] VS Code (version 1.51.1)
[✓] Connected device (1 available)

Thanks in advance.

nongtiny commented 3 years ago

I have tried on the Samsung device (Android10). It showed the same error output.

mirkancal commented 3 years ago

I get the same error. Currently using Distance, Calories and Steps, but can't get steps with this plugin.

thomasnilsson commented 3 years ago

The plugin is broken on Android at the moment. But stay tuned.

nongtiny commented 3 years ago

Thank you for your response, sir.

mirkancal commented 3 years ago

@thomasnilsson android newbie here but it can be related to this, https://stackoverflow.com/a/59303535/9779791

I was trying to fit_kit package, saw this exception related to the steps

Added this <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" /> in AndroidManifest.xml but didn't work.

mirkancal commented 3 years ago

After I've manually turn on the activity permission it worked. Is there anyway we can add this permission to package?

rohitner commented 3 years ago

Modify the requestAuthorization function in health_factory.dart as follows

Future<bool> requestAuthorization(List<HealthDataType> types) async {
    /// If BMI is requested, then also ask for weight and height
    if (types.contains(HealthDataType.BODY_MASS_INDEX)) {
      types.add(HealthDataType.WEIGHT);
      types.add(HealthDataType.HEIGHT);
      types = types.toSet().toList();
    }

    /// app permissions for getting step count
    var androidInfo = await DeviceInfoPlugin().androidInfo;
    int sdkInt = androidInfo.version.sdkInt;

    if (sdkInt > 28) {
      bool activityRecognitionStatus =
      await Permission.activityRecognition.status.isGranted;

      if (!activityRecognitionStatus) {
        if (await Permission.activityRecognition.request().isGranted) {}
      }
    }

    List<String> keys = types.map((e) => _enumToString(e)).toList();
    final bool isAuthorized =
        await _channel.invokeMethod('requestAuthorization', {'types': keys});
    return isAuthorized;
  }

Add the following imports to health.dart

import 'package:permission_handler/permission_handler.dart';
import 'package:device_info/device_info.dart';
thomasnilsson commented 3 years ago

Thanks for the suggestion, I am afraid that we cannot use that solution exactly:

When you need multiple permissions on Android they have to all be requested at once. By hiding the request inside the health library, the user will not be able to bundle it up with the other requests.

The best solution is to let the programmer request the permissions themselves, I would say

thomasnilsson commented 3 years ago

I have been unable to recreate the error on Android 10 (SDK 29). What OS did you guys use?

healthlake commented 3 years ago

Modify the requestAuthorization function in health_factory.dart as follows

Future<bool> requestAuthorization(List<HealthDataType> types) async {
    /// If BMI is requested, then also ask for weight and height
    if (types.contains(HealthDataType.BODY_MASS_INDEX)) {
      types.add(HealthDataType.WEIGHT);
      types.add(HealthDataType.HEIGHT);
      types = types.toSet().toList();
    }

    /// app permissions for getting step count
    var androidInfo = await DeviceInfoPlugin().androidInfo;
    int sdkInt = androidInfo.version.sdkInt;

    if (sdkInt > 28) {
      bool activityRecognitionStatus =
      await Permission.activityRecognition.status.isGranted;

      if (!activityRecognitionStatus) {
        if (await Permission.activityRecognition.request().isGranted) {}
      }
    }

    List<String> keys = types.map((e) => _enumToString(e)).toList();
    final bool isAuthorized =
        await _channel.invokeMethod('requestAuthorization', {'types': keys});
    return isAuthorized;
  }

Add the following imports to health.dart

import 'package:permission_handler/permission_handler.dart';
import 'package:device_info/device_info.dart';

I made this change but it is still hanging up at the following line of code.

final bool isAuthorized =
    await _channel.invokeMethod('requestAuthorization', {'types': keys});

It takes forever but doesn't come back.

In main.dart, I'm just looking for steps type as follows.

List<HealthDataType> types = [
  HealthDataType.STEPS,
];

Not sure what did I miss?

bajpaijalaj commented 3 years ago

I have been unable to recreate the error on Android 10 (SDK 29). What OS did you guys use?

@thomasnilsson It doesn't work for me on - Device: Redmi Note 8 Pro (Android Version 10 QP1A.190711.020)

thomasnilsson commented 3 years ago

Modify the requestAuthorization function in health_factory.dart as follows

Future<bool> requestAuthorization(List<HealthDataType> types) async {
    /// If BMI is requested, then also ask for weight and height
    if (types.contains(HealthDataType.BODY_MASS_INDEX)) {
      types.add(HealthDataType.WEIGHT);
      types.add(HealthDataType.HEIGHT);
      types = types.toSet().toList();
    }

    /// app permissions for getting step count
    var androidInfo = await DeviceInfoPlugin().androidInfo;
    int sdkInt = androidInfo.version.sdkInt;

    if (sdkInt > 28) {
      bool activityRecognitionStatus =
      await Permission.activityRecognition.status.isGranted;

      if (!activityRecognitionStatus) {
        if (await Permission.activityRecognition.request().isGranted) {}
      }
    }

    List<String> keys = types.map((e) => _enumToString(e)).toList();
    final bool isAuthorized =
        await _channel.invokeMethod('requestAuthorization', {'types': keys});
    return isAuthorized;
  }

Add the following imports to health.dart

import 'package:permission_handler/permission_handler.dart';
import 'package:device_info/device_info.dart';

I made this change but it is still hanging up at the following line of code.

final bool isAuthorized =
    await _channel.invokeMethod('requestAuthorization', {'types': keys});

It takes forever but doesn't come back.

In main.dart, I'm just looking for steps type as follows.

List<HealthDataType> types = [
  HealthDataType.STEPS,
];

Not sure what did I miss?

That sounds like the error you will get when you have not registered your application with Google APIs, as specified on the plugin page. Follow these links

https://developers.google.com/fit/android/get-api-key

https://console.developers.google.com/flows/enableapi?apiid=fitness&pli=1

healthlake commented 3 years ago

I am using Moto G Fast with Android 10 version.

I have following above two URLs and registered API with proper credentials.

I am running the example app. What should be package name? cachet.plugins.health_example OR cachet.plugins.health

It keeps twirling on permission screen. image

After creating O-Auth client id, do we store the id in project anywhere?

thomasnilsson commented 3 years ago

The google project for the health example app should already be created and you shouldn't have trouble running it.

But in any case the name you use in the Google API console should be same as the android package name found in the manifest of the generated android project. In this case it will be cachet.plugins.health_example

nongtiny commented 3 years ago

@healthlake Switching the Publishing status to In production in the OAuth consent screen setting should solve the issue