cph-cachet / flutter-plugins

A collection of Flutter plugins developed by CACHET
554 stars 681 forks source link

NullPointerException when reading from Health Connect, health ^10.2.0 #950

Open Leonardbd opened 7 months ago

Leonardbd commented 7 months ago

Device / Emulator and OS

Describe the bug

The app crashes when attempting to fetch health data from Health Connect for a start date where there's no data for that date or for any other date prior to the startdate.

To Reproduce

Use the plugin to fetch health data for a date range that includes a start date without any data available in Health Connect prior to this date. The app crashes when attempting to fetch data for the specified start date.

Health().configure(useHealthConnectIfAvailable: true);
var now = DateTime.now();

var types = [
      HealthDataType.NUTRITION,
    ];
    bool requested = await Health().requestAuthorization(types);

List<HealthDataPoint> healthData = await Health().getHealthDataFromTypes(
        startTime: now.subtract(Duration(days: 4)), endTime: now, types: types);

So today is April 14th, StartTime is then April 10th. The first recorded data available in Health Connect is April 11th, causing it to crash.

Expected behavior

The plugin should handle the scenario gracefully and not crash when attempting to fetch health data for a start date without any available data. Instead of crashing, it should continue to retrieve data for the remaining duration within the specified date range (between the start date and end date)

Actual behavior

The app crashes when attempting to fetch health data for a start date without any available data.

E/AndroidRuntime(17369): FATAL EXCEPTION: main
E/AndroidRuntime(17369): Process: com.applitic.healthtrackerapp, PID: 17369
E/AndroidRuntime(17369): java.lang.NullPointerException
E/AndroidRuntime(17369):    at cachet.plugins.health.HealthPlugin.convertRecord(HealthPlugin.kt:3297)
E/AndroidRuntime(17369):    at cachet.plugins.health.HealthPlugin$getHCData$1.invokeSuspend(HealthPlugin.kt:2828)
E/AndroidRuntime(17369):    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
E/AndroidRuntime(17369):    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
E/AndroidRuntime(17369):    at android.os.Handler.handleCallback(Handler.java:942)
E/AndroidRuntime(17369):    at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(17369):    at android.os.Looper.loopOnce(Looper.java:201)
E/AndroidRuntime(17369):    at android.os.Looper.loop(Looper.java:288)
E/AndroidRuntime(17369):    at android.app.ActivityThread.main(ActivityThread.java:7872)
E/AndroidRuntime(17369):    at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(17369):    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
E/AndroidRuntime(17369):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
E/AndroidRuntime(17369):    Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@e0bf177, Dispatchers.Main]
I/Process (17369): Sending signal. PID: 17369 SIG: 9
Lost connection to device.

Exited.

Flutter doctor

Doctor summary (to see all details, run flutter doctor -v): [√] Flutter (Channel stable, 3.16.5, on Microsoft Windows [Version 10.0.22631.3447], locale en-GB) [√] Windows Version (Installed version of Windows is version 10 or higher) [√] Android toolchain - develop for Android devices (Android SDK version 34.0.0) [√] Chrome - develop for the web [X] Visual Studio - develop Windows apps X Visual Studio not installed; this is necessary to develop Windows apps. Download at https://visualstudio.microsoft.com/downloads/. Please install the "Desktop development with C++" workload, including all of its default components [√] Android Studio (version 2023.1) [√] VS Code (version 1.88.1) [√] Connected device (4 available) [√] Network resources

! Doctor found issues in 1 category.

nwhite872 commented 6 months ago

Hello, I ran into the same problem and I had an afternoon so I read through the file. The plugin passess the NullPointerException which leads to an issue, so I modified the plugin file on line 3284 of '\AppData\Local\Pub\Cache\hosted\pub.dev\health-10.2.0\android\src\main\kotlin\cachet\plugins\health\HealthPlugin.kt from:

is NutritionRecord ->
    return listOf(
        mapOf<String, Any>(
            "calories" to record.energy!!.inKilocalories,
            "protein" to record.protein!!.inGrams,
            "carbs" to record.totalCarbohydrate!!.inGrams,
            "fat" to record.totalFat!!.inGrams,
            "name" to record.name!!,
            "mealType" to (MapTypeToMealTypeHC[record.mealType] ?: MEAL_TYPE_UNKNOWN),
            "date_from" to record.startTime.toEpochMilli(),
            "date_to" to record.endTime.toEpochMilli(),
            "source_id" to "",
            "source_name" to metadata.dataOrigin.packageName,
        )
    )

Into

is NutritionRecord -> {
  val calories = record.energy?.inKilocalories ?: 0.0
  val protein = record.protein?.inGrams ?: 0.0
  val carbs = record.totalCarbohydrate?.inGrams ?: 0.0
  val fat = record.totalFat?.inGrams ?: 0.0
  val name = record.name ?: ""
  val mealType = MapTypeToMealTypeHC[record.mealType] ?: MEAL_TYPE_UNKNOWN
  val dateFrom = record.startTime?.toEpochMilli() ?: 0L
  val dateTo = record.endTime?.toEpochMilli() ?: 0L
  val sourceId = ""
  val sourceName = metadata.dataOrigin?.packageName ?: ""

  return listOf(
  mapOf(
  "calories" to calories,
  "protein" to protein,
  "carbs" to carbs,
  "fat" to fat,
  "name" to name,
  "mealType" to mealType,
  "date_from" to dateFrom,
  "date_to" to dateTo,
  "source_id" to sourceId,
  "source_name" to sourceName,
  )
   )
}

Admittedly it is not elegant, but it sets the default values to 0.0 for missing values in carbohydrates/protein/fats which was what was causing my problem. The formating in the HealthPlugin.kt file drove me insane.

I hope this helps. If someone more skilled than I would be kind enough to pull request that would be appreciated.

drb1010 commented 4 months ago

same error and app crashes in android only, on ios it is working perfectly.Also It works on android if startdate is subtracted by days 3 only

shubhamsinghmutualmobile commented 3 months ago

Hello, I ran into the same problem and I had an afternoon so I read through the file. The plugin passess the NullPointerException which leads to an issue, so I modified the plugin file on line 3284 of '\AppData\Local\Pub\Cache\hosted\pub.dev\health-10.2.0\android\src\main\kotlin\cachet\plugins\health\HealthPlugin.kt from:

is NutritionRecord ->
    return listOf(
        mapOf<String, Any>(
            "calories" to record.energy!!.inKilocalories,
            "protein" to record.protein!!.inGrams,
            "carbs" to record.totalCarbohydrate!!.inGrams,
            "fat" to record.totalFat!!.inGrams,
            "name" to record.name!!,
            "mealType" to (MapTypeToMealTypeHC[record.mealType] ?: MEAL_TYPE_UNKNOWN),
            "date_from" to record.startTime.toEpochMilli(),
            "date_to" to record.endTime.toEpochMilli(),
            "source_id" to "",
            "source_name" to metadata.dataOrigin.packageName,
        )
    )

Into

is NutritionRecord -> {
  val calories = record.energy?.inKilocalories ?: 0.0
  val protein = record.protein?.inGrams ?: 0.0
  val carbs = record.totalCarbohydrate?.inGrams ?: 0.0
  val fat = record.totalFat?.inGrams ?: 0.0
  val name = record.name ?: ""
  val mealType = MapTypeToMealTypeHC[record.mealType] ?: MEAL_TYPE_UNKNOWN
  val dateFrom = record.startTime?.toEpochMilli() ?: 0L
  val dateTo = record.endTime?.toEpochMilli() ?: 0L
  val sourceId = ""
  val sourceName = metadata.dataOrigin?.packageName ?: ""

  return listOf(
  mapOf(
  "calories" to calories,
  "protein" to protein,
  "carbs" to carbs,
  "fat" to fat,
  "name" to name,
  "mealType" to mealType,
  "date_from" to dateFrom,
  "date_to" to dateTo,
  "source_id" to sourceId,
  "source_name" to sourceName,
  )
   )
}

Admittedly it is not elegant, but it sets the default values to 0.0 for missing values in carbohydrates/protein/fats which was what was causing my problem. The formating in the HealthPlugin.kt file drove me insane.

I hope this helps. If someone more skilled than I would be kind enough to pull request that would be appreciated.

I'm facing this same issue. Could you please help out with this @bardram ?

shubhamsinghmutualmobile commented 3 months ago

For me, the temporary solution was to explicitly set all unneeded values to 0 when using the writeMeal method:

final time = DateTime.now();
final wasWriteSuccessful = await health.writeMeal(
  mealType: MealType.SNACK,
  startTime: time.subtract(const Duration(seconds: 1)),
  endTime: time,
  name: healthPreset.name ?? healthPreset.beverage,
  caffeine: (int.parse(healthPreset.caffeine.toString())) / 1000,
  caloriesConsumed: 0, // Explicitly add this to avoid the `null pointer exception` crash
  carbohydrates: 0, // Explicitly add this to avoid the `null pointer exception` crash
  fatTotal: 0, // Explicitly add this to avoid the `null pointer exception` crash
  protein: 0, // Explicitly add this to avoid the `null pointer exception` crash
);
if (!wasWriteSuccessful) {
  log('Caffeine write was NOT successful!');
}

Make sure to delete all previously written data via health: 10.2.0 to avoid seeing the same crash because of that previous/incompatible data.