Reputation: 11
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