Clover99
Clover99

Reputation: 11

Flutter Health Plugin: Permission Not Granted When Reading Step Data

I am trying to access step data using Flutter's health plugin, but I keep encountering the issue where permissions are not granted, even though I have declared the necessary permissions in my AndroidManifest

I have implemented the following function to request permissions and access step data. However, requestAuthorization() always returns false

Future<bool> _requestPermissions() async {
  try {
    // First check if Health Connect is available
    bool isHealthConnectAvailable = await health.isHealthConnectAvailable();
    print("Health Connect available: $isHealthConnectAvailable");

    if (!isHealthConnectAvailable) {
      print("Health Connect is not available on this device");
      return false;
    }

    // Define all the data types we want to access
    final types = [HealthDataType.STEPS];

    // Set up corresponding permissions
    final permissions =
        types.map((type) => HealthDataAccess.READ_WRITE).toList();

    print("Checking existing permissions for health data types: $types");
    // I thought if i had declared it in manifest i would get it true here. But apparently not
    // Check if we already have permissions

    bool? hasAllPermissions = await health.hasPermissions(types);

    print("Existing permissions status: $hasAllPermissions");

    // If we already have all permissions, no need to request them again
    if (hasAllPermissions == true) {
      print("Already have all required permissions!");
      return true;
    }

    // Request Activity Recognition permission
    var activityStatus = await Permission.activityRecognition.status;
    print("Activity Recognition initial status: ${activityStatus.name}");

    if (!activityStatus.isGranted) {
      final status = await Permission.activityRecognition.request();
      await Permission.locationWhenInUse.request();
      await Permission.sensors.request();
      print("Activity Recognition request result: ${status.name}");
      if (!status.isGranted) {
        print("Activity Recognition permission denied: ${status.name}");
        return false;
      }
    }

    print("Requesting authorization for health data types: $types");

    try {
      // Check if types are supported before requesting authorization
      for (HealthDataType type in types) {
        bool supported = await health.isDataTypeAvailable(type);
        print("Health data type $type supported: $supported");
        if (!supported) {
          print("Warning: $type is not supported on this device");
        }
      }

      // Request Health Connect permissions with both read and write access
      // always returns false
      bool authorized = await health.requestAuthorization(
        types,
        permissions: permissions,
      );

      print("Health authorization result: $authorized");

      if (!authorized) {
        print("Health permissions not granted!");
        return false;
      }

      // Verify if we can actually read the data types
      for (HealthDataType type in types) {
        bool isAvailable = await health.isDataTypeAvailable(type);
        print("Health data type $type available: $isAvailable");
      }

      print("Health permissions granted successfully!");
      return true;
    } catch (healthErr) {
      print("Error requesting Health API permissions: $healthErr");
      return false;
    }
  } catch (e) {
    print("General error in permission request: $e");
    return false;
  }
}

here is my android manifest.xml file

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Required Permissions -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.BODY_SENSORS" />
    
    <!-- Health Connect Permissions -->
    <uses-permission android:name="android.permission.health.READ_STEPS" />
    <uses-permission android:name="android.permission.health.WRITE_STEPS" />
    <uses-permission android:name="android.permission.health.READ_DISTANCE" />
    <uses-permission android:name="android.permission.health.WRITE_DISTANCE" />
    <uses-permission android:name="android.permission.health.READ_TOTAL_CALORIES_BURNED" />
    <uses-permission android:name="android.permission.health.WRITE_TOTAL_CALORIES_BURNED" />
    
    <!-- Legacy Google Fit permissions (as fallback) -->
    <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />

    <!-- Queries for Health Connect -->
    <queries>
        <package android:name="com.google.android.apps.healthdata" />
        <intent>
            <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
        </intent>
    </queries>

    <application
        android:label="MyAppl"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">

        <!-- Main Activity -->
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">

            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme"
            />

            <!-- Main app intent filter -->
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>

            <!-- Health Connect intent filter -->
            <intent-filter>
                <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
            </intent-filter>
        </activity>

        <!-- Health Connect Permission Usage Activity -->
        <activity-alias
            android:name=".ViewPermissionUsageActivity"
            android:exported="true"
            android:targetActivity=".MainActivity"
            android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
            <intent-filter>
                <action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
                <category android:name="android.intent.category.HEALTH_PERMISSIONS" />
            </intent-filter>
        </activity-alias>

        <!-- Flutter embedding -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />

        <!-- Health Connect metadata -->
        <meta-data 
            android:name="health_permissions" 
            android:resource="@array/health_permissions" />
    </application>
</manifest>

Here is the requestAuthorization function:

Future<bool> requestAuthorization(
    List<HealthDataType> types, {
    List<HealthDataAccess>? permissions,
  }) async {
    await _checkIfHealthConnectAvailableOnAndroid();
    if (permissions != null && permissions.length != types.length) {
      throw ArgumentError(
          'The length of [types] must be same as that of [permissions].');
    }

    if (permissions != null) {
      for (int i = 0; i < types.length; i++) {
        final type = types[i];
        final permission = permissions[i];
        if ((type == HealthDataType.ELECTROCARDIOGRAM ||
                type == HealthDataType.HIGH_HEART_RATE_EVENT ||
                type == HealthDataType.LOW_HEART_RATE_EVENT ||
                type == HealthDataType.IRREGULAR_HEART_RATE_EVENT ||
                type == HealthDataType.WALKING_HEART_RATE ||
                type == HealthDataType.ATRIAL_FIBRILLATION_BURDEN) &&
            permission != HealthDataAccess.READ) {
          throw ArgumentError(
              'Requesting WRITE permission on ELECTROCARDIOGRAM / HIGH_HEART_RATE_EVENT / LOW_HEART_RATE_EVENT / IRREGULAR_HEART_RATE_EVENT / WALKING_HEART_RATE / ATRIAL_FIBRILLATION_BURDEN is not allowed.');
        }
      }
    }

    final mTypes = List<HealthDataType>.from(types, growable: true);
    final mPermissions = permissions == null
        ? List<int>.filled(types.length, HealthDataAccess.READ.index,
            growable: true)
        : permissions.map((permission) => permission.index).toList();

    // on Android, if BMI is requested, then also ask for weight and height
    if (Platform.isAndroid) _handleBMI(mTypes, mPermissions);

    List<String> keys = mTypes.map((e) => e.name).toList();
    final bool? isAuthorized = await _channel.invokeMethod(
        'requestAuthorization', {'types': keys, "permissions": mPermissions});
    return isAuthorized ?? false;
  }

I Ensured that:

So my questions if anyone could help me with this issue are:

Upvotes: 1

Views: 20

Answers (0)

Related Questions