Skip to content

Commit

Permalink
Merge pull request #1117 from cph-cachet/iarata/health-12
Browse files Browse the repository at this point in the history
Health 12.0.0
  • Loading branch information
bardram authored Jan 15, 2025
2 parents ff95530 + 7c33275 commit fcac28a
Show file tree
Hide file tree
Showing 21 changed files with 843 additions and 383 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,5 @@ app.*.symbols
!/dev/ci/**/Gemfile.lock
packages/app_usage/example/.flutter-plugins-dependencies
packages/app_usage/example/.flutter-plugins-dependencies

.sdkmanrc
31 changes: 31 additions & 0 deletions packages/health/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
## 12.0.0

* **BREAKING** This release introduces a significant architectural change to the `health` plugin by removing the `singleton` pattern.
* **Dependency Injection for `DeviceInfoPlugin`**:
- The `Health` class is no longer a singleton.
- The `Health()` factory constructor is removed.
- The `Health` class now accepts an (optional) `DeviceInfoPlugin` dependency through its constructor, this change was introduced to provide easy mocking of the `DeviceInfo` class during unit tests.
- This architectural change means that, for the application to work correctly, the `Health` class *MUST* be initialized correctly as a global instance.
* **Impact**:
- For most users, **no immediate code changes are required** but it is paramount to initialize the `Health` class as a global instance (i.e. do not call `Health()` every time but rather define an instance `final health = Health();`).
* **BREAKING** (Android) Remove automatic permission request of `DISTANCE_DELTA` and `TOTAL_CALORIES_BURNED` data types when requesting permission for `WORKOUT` health data type.
* For `WORKOUT`s that require above permissions, now those need to be requested manually.
* Fix [#984](https://github.com/cph-cachet/flutter-plugins/issues/984) - PR [#1055](https://github.com/cph-cachet/flutter-plugins/pull/1055)
* Add `LEAN_BODY_MASS` data type [#1078](https://github.com/cph-cachet/flutter-plugins/issues/1078) - PR [#1097](https://github.com/cph-cachet/flutter-plugins/pull/1097)
* The following AndroidManifest values are required to READ/WRITE `LEAN_BODY_MASS`:
```XML
<uses-permission android:name="android.permission.health.READ_LEAN_BODY_MASS"/>
<uses-permission android:name="android.permission.health.WRITE_LEAN_BODY_MASS"/>
```
* iOS: Add `WATER_TEMPERATURE` and `UNDERWATER_DEPTH` health values [#1096](https://github.com/cph-cachet/flutter-plugins/issues/1096)
* iOS: Add support for `Underwater Diving` workout [#1096](https://github.com/cph-cachet/flutter-plugins/issues/1096)
* Fix [#1072](https://github.com/cph-cachet/flutter-plugins/issues/1072) and [#1074](https://github.com/cph-cachet/flutter-plugins/issues/1074)
* Fix issue where iOS delete not deleting own records - PR [#1104](https://github.com/cph-cachet/flutter-plugins/pull/1104)
* Fix [#950](https://github.com/cph-cachet/flutter-plugins/issues/950) - PR [#1103](https://github.com/cph-cachet/flutter-plugins/pull/1103)
* Fix [#1047](https://github.com/cph-cachet/flutter-plugins/issues/1047) and [#939](https://github.com/cph-cachet/flutter-plugins/issues/939) - PR [#1091](https://github.com/cph-cachet/flutter-plugins/pull/1091)
* Fix issue where `SLEEP_LIGHT` type was not aligned correctly - PR [#1086](https://github.com/cph-cachet/flutter-plugins/pull/1086)
* Fix [#1051](https://github.com/cph-cachet/flutter-plugins/issues/1051) - PR [#1052](https://github.com/cph-cachet/flutter-plugins/pull/1052)
* Updated `intl` to ^0.20.1 [#1092](https://github.com/cph-cachet/flutter-plugins/issues/1092)
* Updated `device_info_plus` to ^11.2.0
* Example app: Updated `permission_handler` to ^11.3.1

## 11.1.1

* Fix of [#1059](https://github.com/cph-cachet/flutter-plugins/issues/1059)
Expand Down
5 changes: 5 additions & 0 deletions packages/health/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,10 @@ The plugin supports the following [`HealthDataType`](https://pub.dev/documentati
| ELECTROCARDIOGRAM | VOLT | yes | | Requires Apple Watch to write the data |
| NUTRITION | NO_UNIT | yes | yes | |
| INSULIN_DELIVERY | INTERNATIONAL_UNIT | yes | | |
| MENSTRUATION_FLOW | NO_UNIT | yes | yes | |
| WATER_TEMPERATURE | DEGREE_CELSIUS | yes | | Related to/Requires Apple Watch Ultra's Underwater Diving Workout |
| UNDERWATER_DEPTH | METER | yes | | Related to/Requires Apple Watch Ultra's Underwater Diving Workout |
| LEAN_BODY_MASS | KILOGRAMS | yes | yes | |

## Workout Types

Expand Down Expand Up @@ -443,6 +447,7 @@ The plugin supports the following [`HealthWorkoutActivityType`](https://pub.dev/
| TENNIS | yes | yes | |
| TRACK_AND_FIELD | yes | | |
| TRADITIONAL_STRENGTH_TRAINING | yes | (yes) | on Android this will be stored as STRENGTH_TRAINING |
| UNDERWATER_DIVING | yes | | |
| VOLLEYBALL | yes | yes | |
| WALKING | yes | yes | |
| WATER_FITNESS | yes | | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const val BLOOD_OXYGEN = "BLOOD_OXYGEN"
const val BLOOD_PRESSURE_DIASTOLIC = "BLOOD_PRESSURE_DIASTOLIC"
const val BLOOD_PRESSURE_SYSTOLIC = "BLOOD_PRESSURE_SYSTOLIC"
const val BODY_FAT_PERCENTAGE = "BODY_FAT_PERCENTAGE"
const val LEAN_BODY_MASS = "LEAN_BODY_MASS"
const val BODY_TEMPERATURE = "BODY_TEMPERATURE"
const val BODY_WATER_MASS = "BODY_WATER_MASS"
const val DISTANCE_DELTA = "DISTANCE_DELTA"
Expand Down Expand Up @@ -544,38 +545,6 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
),
)
}
// Workout also needs distance and total energy burned too
if (typeKey == WORKOUT) {
if (access == 0) {
permList.addAll(
listOf(
HealthPermission.getReadPermission(
DistanceRecord::class
),
HealthPermission.getReadPermission(
TotalCaloriesBurnedRecord::class
),
),
)
} else {
permList.addAll(
listOf(
HealthPermission.getReadPermission(
DistanceRecord::class
),
HealthPermission.getReadPermission(
TotalCaloriesBurnedRecord::class
),
HealthPermission.getWritePermission(
DistanceRecord::class
),
HealthPermission.getWritePermission(
TotalCaloriesBurnedRecord::class
),
),
)
}
}
}
scope.launch {
result.success(
Expand Down Expand Up @@ -629,38 +598,6 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
),
)
}
// Workout also needs distance and total energy burned too
if (typeKey == WORKOUT) {
if (access == 0) {
permList.addAll(
listOf(
HealthPermission.getReadPermission(
DistanceRecord::class
),
HealthPermission.getReadPermission(
TotalCaloriesBurnedRecord::class
),
),
)
} else {
permList.addAll(
listOf(
HealthPermission.getReadPermission(
DistanceRecord::class
),
HealthPermission.getReadPermission(
TotalCaloriesBurnedRecord::class
),
HealthPermission.getWritePermission(
DistanceRecord::class
),
HealthPermission.getWritePermission(
TotalCaloriesBurnedRecord::class
),
),
)
}
}
}
if (healthConnectRequestPermissionsLauncher == null) {
result.success(false)
Expand Down Expand Up @@ -1083,6 +1020,29 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
),
)

is LeanBodyMassRecord ->
return listOf(
mapOf<String, Any>(
"uuid" to
metadata.id,
"value" to
record.mass
.inKilograms,
"date_from" to
record.time
.toEpochMilli(),
"date_to" to
record.time
.toEpochMilli(),
"source_id" to "",
"source_name" to
metadata.dataOrigin
.packageName,
"recording_method" to
metadata.recordingMethod
),
)

is StepsRecord ->
return listOf(
mapOf<String, Any>(
Expand Down Expand Up @@ -1516,7 +1476,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
"sugar" to record.sugar?.inGrams,
"water" to null,
"zinc" to record.zinc?.inGrams,
"name" to record.name!!,
"name" to (record.name ?: ""),
"meal_type" to
(mapTypeToMealType[
record.mealType]
Expand Down Expand Up @@ -1596,6 +1556,22 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
),
)

LEAN_BODY_MASS ->
LeanBodyMassRecord(
time =
Instant.ofEpochMilli(
startTime
),
mass =
Mass.kilograms(
value
),
zoneOffset = null,
metadata = Metadata(
recordingMethod = recordingMethod,
),
)

HEIGHT ->
HeightRecord(
time =
Expand Down Expand Up @@ -2398,6 +2374,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
private val mapToType =
hashMapOf(
BODY_FAT_PERCENTAGE to BodyFatRecord::class,
LEAN_BODY_MASS to LeanBodyMassRecord::class,
HEIGHT to HeightRecord::class,
WEIGHT to WeightRecord::class,
STEPS to StepsRecord::class,
Expand Down Expand Up @@ -2438,7 +2415,6 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
// "BasalMetabolicRate" to BasalMetabolicRateRecord::class,
// "BloodGlucose" to BloodGlucoseRecord::class,
// "BloodPressure" to BloodPressureRecord::class,
// "BodyFat" to BodyFatRecord::class,
// "BodyTemperature" to BodyTemperatureRecord::class,
// "BoneMass" to BoneMassRecord::class,
// "CervicalMucus" to CervicalMucusRecord::class,
Expand All @@ -2451,7 +2427,6 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
// "HeartRate" to HeartRateRecord::class,
// "Height" to HeightRecord::class,
// "Hydration" to HydrationRecord::class,
// "LeanBodyMass" to LeanBodyMassRecord::class,
// "MenstruationPeriod" to MenstruationPeriodRecord::class,
// "Nutrition" to NutritionRecord::class,
// "OvulationTest" to OvulationTestRecord::class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
<uses-permission android:name="android.permission.health.BODY_SENSORS"/>
<uses-permission android:name="android.permission.health.READ_MENSTRUATION"/>
<uses-permission android:name="android.permission.health.WRITE_MENSTRUATION"/>
<uses-permission android:name="android.permission.health.READ_LEAN_BODY_MASS"/>
<uses-permission android:name="android.permission.health.WRITE_LEAN_BODY_MASS"/>

<application android:label="health_example"
android:icon="@mipmap/ic_launcher">
Expand Down
52 changes: 49 additions & 3 deletions packages/health/example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
8AB8966E9F27B6C816D51EA9 /* [CP] Embed Pods Frameworks */,
99FD4B47838A33EC942FDC35 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
Expand Down Expand Up @@ -271,6 +272,24 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
99FD4B47838A33EC942FDC35 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/permission_handler_apple/permission_handler_apple_privacy.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/permission_handler_apple_privacy.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
AFF7CCF5217A091E1625CD54 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
Expand All @@ -293,6 +312,24 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
FEE6262278064D703A8D8D21 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/permission_handler_apple/permission_handler_apple_privacy.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/permission_handler_apple_privacy.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
Expand Down Expand Up @@ -394,7 +431,10 @@
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
Expand Down Expand Up @@ -531,7 +571,10 @@
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
Expand Down Expand Up @@ -564,7 +607,10 @@
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
Expand Down
2 changes: 1 addition & 1 deletion packages/health/example/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import UIKit
import Flutter

@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
Expand Down
Loading

0 comments on commit fcac28a

Please sign in to comment.