From 1276a399e3887884be2946f1d96501ae7c76be38 Mon Sep 17 00:00:00 2001 From: Mohamad Reza Golab Date: Mon, 3 Jun 2024 20:10:11 +0330 Subject: [PATCH 01/80] fix(future): stream must call asynchronous --- packages/mobility_features/lib/src/mobility_features.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobility_features/lib/src/mobility_features.dart b/packages/mobility_features/lib/src/mobility_features.dart index c0ba592e5..130a3d00a 100644 --- a/packages/mobility_features/lib/src/mobility_features.dart +++ b/packages/mobility_features/lib/src/mobility_features.dart @@ -47,7 +47,7 @@ class MobilityFeatures { if (_subscription != null) { await _subscription!.cancel(); } - _subscription = stream.listen(_onData); + _subscription = await stream.listen(_onData); } Future _handleInit() async { From aa1fb819dc788db7ee2d2f5a95f2f50303493927 Mon Sep 17 00:00:00 2001 From: Mohamad Reza Golab Date: Tue, 4 Jun 2024 10:48:42 +0330 Subject: [PATCH 02/80] fix(permissions): add some permissions --- .../android/app/src/main/AndroidManifest.xml | 4 ++ .../mobility_features/example/lib/main.dart | 72 ++++++++++++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml b/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml index df58824d8..46f755ecd 100644 --- a/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml +++ b/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml @@ -7,12 +7,16 @@ FlutterApplication and put your custom class here. --> + + + + { /// * Location streaming to MobilityContext /// * Subscribe to MobilityContext updates void streamInit() async { + await _requestNotificationPermission(); + await _requestManageExternalStoragePermission(); + + // ask for location permissions, if not already granted + if (!await isLocationAlwaysGranted()) { + await _requestLocationPermission(); + await askForLocationAlwaysPermission(); + } + locationStream = LocationManager().locationStream; // subscribe to location stream - in case this is needed in the app @@ -121,11 +131,69 @@ class _MyHomePageState extends State { DateTime.now())); // provide the [MobilityFeatures] instance with the LocationSample stream - MobilityFeatures().startListening(locationSampleStream); + await MobilityFeatures().startListening(locationSampleStream); // start listening to incoming MobilityContext objects mobilitySubscription = - MobilityFeatures().contextStream.listen(onMobilityContext); + await MobilityFeatures().contextStream.listen(onMobilityContext); + } + + Future isLocationAlwaysGranted() async { + bool granted = false; + try { + granted = await Permission.locationAlways.isGranted; + } catch (e) { + print(e); + } + return granted; + } + + /// Tries to ask for "location always" permissions from the user. + /// Returns `true` if successful, `false` otherwise. + Future askForLocationAlwaysPermission() async { + bool granted = false; + try { + granted = await Permission.locationAlways.isGranted; + } catch (e) { + print(e); + } + + if (!granted) { + granted = + await Permission.locationAlways.request() == PermissionStatus.granted; + } + + return granted; + } + + Future _requestLocationPermission() async { + final result = await Permission.location.request(); + + if (result == PermissionStatus.granted) { + print('GRANTED'); // ignore: avoid_print + } else { + print('NOT GRANTED'); // ignore: avoid_print + } + } + + Future _requestNotificationPermission() async { + final result = await Permission.notification.request(); + + if (result == PermissionStatus.granted) { + print('NOTIFICATION GRANTED'); + } else { + print('NOTIFICATION NOT GRANTED'); + } + } + + Future _requestManageExternalStoragePermission() async { + final result = await Permission.manageExternalStorage.request(); + + if (result == PermissionStatus.granted) { + print('MANAGE_EXTERNAL_STORAGE GRANTED'); + } else { + print('MANAGE_EXTERNAL_STORAGE NOT GRANTED'); + } } /// Called whenever location changes. From d30bbe33ea195b7f3c8f95987bb24c4c5008478a Mon Sep 17 00:00:00 2001 From: Mohamad Reza Golab Date: Tue, 4 Jun 2024 10:51:18 +0330 Subject: [PATCH 03/80] fix(dependency): carp_background_location is depend on background_locator_2 --- .../android/app/src/main/AndroidManifest.xml | 33 ++++--------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml b/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml index 46f755ecd..5a00b5546 100644 --- a/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml +++ b/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml @@ -53,37 +53,18 @@ - - - + + - - - - - - + - + android:foregroundServiceType = "location"/> From 41e629fd6538301d65b8b77a56145a8f94e1c21d Mon Sep 17 00:00:00 2001 From: Mohamad Reza Golab Date: Tue, 4 Jun 2024 10:59:48 +0330 Subject: [PATCH 04/80] fix(nullSafety): can not compare null with zero --- packages/mobility_features/example/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobility_features/example/lib/main.dart b/packages/mobility_features/example/lib/main.dart index 47216b57a..1a697d748 100644 --- a/packages/mobility_features/example/lib/main.dart +++ b/packages/mobility_features/example/lib/main.dart @@ -226,7 +226,7 @@ class _MyHomePageState extends State { "${_mobilityContext.numberOfSignificantPlaces}", placeIcon), entry( "Home Stay", - _mobilityContext.homeStay! < 0 + _mobilityContext.homeStay == null || _mobilityContext.homeStay! < 0 ? "?" : "${(_mobilityContext.homeStay! * 100).toStringAsFixed(1)}%", homeStayIcon), From b9dc8231c34434cbb419216f448dfc152adbcf58 Mon Sep 17 00:00:00 2001 From: Mohamad Reza Golab Date: Tue, 4 Jun 2024 12:01:05 +0330 Subject: [PATCH 05/80] fix(permissions): ask user for Activity Recognition permission --- packages/pedometer/example/lib/main.dart | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/pedometer/example/lib/main.dart b/packages/pedometer/example/lib/main.dart index 1310d2c6e..2139f634b 100644 --- a/packages/pedometer/example/lib/main.dart +++ b/packages/pedometer/example/lib/main.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'dart:async'; import 'package:pedometer/pedometer.dart'; +import 'package:permission_handler/permission_handler.dart'; String formatDate(DateTime d) { return d.toString().substring(0, 19); @@ -56,10 +57,25 @@ class _MyAppState extends State { }); } - void initPlatformState() { + Future _checkActivityRecognitionPermission() async { + bool granted = await Permission.activityRecognition.isGranted; + + if (!granted) { + granted = await Permission.activityRecognition.request() == + PermissionStatus.granted; + } + + return granted; + } + + Future initPlatformState() async { + bool granted = await _checkActivityRecognitionPermission(); + if (!granted) { + // tell user, the app will not work + } + _pedestrianStatusStream = Pedometer.pedestrianStatusStream; - _pedestrianStatusStream - .listen(onPedestrianStatusChanged) + (await _pedestrianStatusStream.listen(onPedestrianStatusChanged)) .onError(onPedestrianStatusError); _stepCountStream = Pedometer.stepCountStream; From 87470752539ea0eccaf7efff627cf6ae912f6c65 Mon Sep 17 00:00:00 2001 From: bps-takumaodaki <111839744+bps-takumaodaki@users.noreply.github.com> Date: Wed, 24 Jul 2024 23:05:34 +0900 Subject: [PATCH 06/80] Fix behavior when calling unimplemented method channel API on iOS (#998) --- packages/health/lib/src/health_plugin.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index cb755553c..1d22dd68e 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -54,13 +54,12 @@ class Health { /// If [useHealthConnectIfAvailable] is true, Google Health Connect on /// Android will be used. Has no effect on iOS. Future configure({bool useHealthConnectIfAvailable = false}) async { - _deviceId ??= Platform.isAndroid - ? (await _deviceInfo.androidInfo).id - : (await _deviceInfo.iosInfo).identifierForVendor; - - _useHealthConnectIfAvailable = useHealthConnectIfAvailable; - if (_useHealthConnectIfAvailable) { + if (Platform.isAndroid) { + _deviceId = (await _deviceInfo.androidInfo).id; + _useHealthConnectIfAvailable = useHealthConnectIfAvailable; await _channel.invokeMethod('useHealthConnectIfAvailable'); + } else { + _deviceId = (await _deviceInfo.iosInfo).identifierForVendor; } } From 15cc5a3455608abfb704159993f834319793b8b2 Mon Sep 17 00:00:00 2001 From: Watcharachai Kanjaikaew Date: Thu, 25 Jul 2024 20:59:39 +0700 Subject: [PATCH 07/80] feat: add exception when reading from HC (#981) --- .../cachet/plugins/health/HealthPlugin.kt | 467 +++++++++--------- 1 file changed, 240 insertions(+), 227 deletions(-) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 323c157c4..eba81d4af 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -2313,7 +2313,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ) result.success(stepsInInterval) } catch (e: Exception) { - Log.i("FLUTTER_HEALTH::ERROR", "unable to return steps") + Log.e("FLUTTER_HEALTH::ERROR", "Unable to return steps due to the following exception:") + Log.e("FLUTTER_HEALTH::ERROR", Log.getStackTraceString(e)) result.success(null) } } @@ -2633,204 +2634,210 @@ class HealthPlugin(private var channel: MethodChannel? = null) : val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) val healthConnectData = mutableListOf>() scope.launch { - MapToHCType[dataType]?.let { classType -> - val records = mutableListOf() - - // Set up the initial request to read health records with specified - // parameters - var request = - ReadRecordsRequest( - recordType = classType, - // Define the maximum amount of data - // that HealthConnect can return - // in a single request - timeRangeFilter = - TimeRangeFilter.between( - startTime, - endTime - ), - ) - - var response = healthConnectClient.readRecords(request) - var pageToken = response.pageToken - - // Add the records from the initial response to the records list - records.addAll(response.records) + try { + MapToHCType[dataType]?.let { classType -> + val records = mutableListOf() - // Continue making requests and fetching records while there is a - // page token - while (!pageToken.isNullOrEmpty()) { - request = + // Set up the initial request to read health records with specified + // parameters + var request = ReadRecordsRequest( recordType = classType, + // Define the maximum amount of data + // that HealthConnect can return + // in a single request timeRangeFilter = TimeRangeFilter.between( startTime, endTime ), - pageToken = pageToken ) - response = healthConnectClient.readRecords(request) - pageToken = response.pageToken + var response = healthConnectClient.readRecords(request) + var pageToken = response.pageToken + + // Add the records from the initial response to the records list records.addAll(response.records) - } - // Workout needs distance and total calories burned too - if (dataType == WORKOUT) { - for (rec in records) { - val record = rec as ExerciseSessionRecord - val distanceRequest = - healthConnectClient.readRecords( - ReadRecordsRequest( - recordType = - DistanceRecord::class, - timeRangeFilter = - TimeRangeFilter.between( - record.startTime, - record.endTime, - ), - ), + // Continue making requests and fetching records while there is a + // page token + while (!pageToken.isNullOrEmpty()) { + request = + ReadRecordsRequest( + recordType = classType, + timeRangeFilter = + TimeRangeFilter.between( + startTime, + endTime + ), + pageToken = pageToken ) - var totalDistance = 0.0 - for (distanceRec in distanceRequest.records) { - totalDistance += - distanceRec.distance - .inMeters - } + response = healthConnectClient.readRecords(request) - val energyBurnedRequest = - healthConnectClient.readRecords( - ReadRecordsRequest( - recordType = - TotalCaloriesBurnedRecord::class, - timeRangeFilter = - TimeRangeFilter.between( - record.startTime, - record.endTime, - ), - ), - ) - var totalEnergyBurned = 0.0 - for (energyBurnedRec in - energyBurnedRequest.records) { - totalEnergyBurned += - energyBurnedRec.energy - .inKilocalories - } + pageToken = response.pageToken + records.addAll(response.records) + } - val stepRequest = - healthConnectClient.readRecords( - ReadRecordsRequest( - recordType = - StepsRecord::class, - timeRangeFilter = - TimeRangeFilter.between( - record.startTime, - record.endTime - ), - ), - ) - var totalSteps = 0.0 - for (stepRec in stepRequest.records) { - totalSteps += stepRec.count - } + // Workout needs distance and total calories burned too + if (dataType == WORKOUT) { + for (rec in records) { + val record = rec as ExerciseSessionRecord + val distanceRequest = + healthConnectClient.readRecords( + ReadRecordsRequest( + recordType = + DistanceRecord::class, + timeRangeFilter = + TimeRangeFilter.between( + record.startTime, + record.endTime, + ), + ), + ) + var totalDistance = 0.0 + for (distanceRec in distanceRequest.records) { + totalDistance += + distanceRec.distance + .inMeters + } - // val metadata = (rec as Record).metadata - // Add final datapoint - healthConnectData.add( - // mapOf( - mapOf( - "workoutActivityType" to - (workoutTypeMapHealthConnect - .filterValues { - it == - record.exerciseType - } - .keys - .firstOrNull() - ?: "OTHER"), - "totalDistance" to - if (totalDistance == - 0.0 - ) - null - else - totalDistance, - "totalDistanceUnit" to - "METER", - "totalEnergyBurned" to - if (totalEnergyBurned == - 0.0 - ) - null - else - totalEnergyBurned, - "totalEnergyBurnedUnit" to - "KILOCALORIE", - "totalSteps" to - if (totalSteps == - 0.0 - ) - null - else - totalSteps, - "totalStepsUnit" to - "COUNT", - "unit" to "MINUTES", - "date_from" to - rec.startTime - .toEpochMilli(), - "date_to" to - rec.endTime.toEpochMilli(), - "source_id" to "", - "source_name" to - record.metadata - .dataOrigin - .packageName, - ), - ) - } - // Filter sleep stages for requested stage - } else if (classType == SleepSessionRecord::class) { - for (rec in response.records) { - if (rec is SleepSessionRecord) { - if (dataType == SLEEP_SESSION) { - healthConnectData.addAll( - convertRecord( - rec, - dataType - ) - ) - } else { - for (recStage in rec.stages) { - if (dataType == - MapSleepStageToType[ - recStage.stage] - ) { - healthConnectData - .addAll( - convertRecordStage( - recStage, - dataType, - rec.metadata.dataOrigin - .packageName - ) - ) + val energyBurnedRequest = + healthConnectClient.readRecords( + ReadRecordsRequest( + recordType = + TotalCaloriesBurnedRecord::class, + timeRangeFilter = + TimeRangeFilter.between( + record.startTime, + record.endTime, + ), + ), + ) + var totalEnergyBurned = 0.0 + for (energyBurnedRec in + energyBurnedRequest.records) { + totalEnergyBurned += + energyBurnedRec.energy + .inKilocalories + } + + val stepRequest = + healthConnectClient.readRecords( + ReadRecordsRequest( + recordType = + StepsRecord::class, + timeRangeFilter = + TimeRangeFilter.between( + record.startTime, + record.endTime + ), + ), + ) + var totalSteps = 0.0 + for (stepRec in stepRequest.records) { + totalSteps += stepRec.count + } + + // val metadata = (rec as Record).metadata + // Add final datapoint + healthConnectData.add( + // mapOf( + mapOf( + "workoutActivityType" to + (workoutTypeMapHealthConnect + .filterValues { + it == + record.exerciseType + } + .keys + .firstOrNull() + ?: "OTHER"), + "totalDistance" to + if (totalDistance == + 0.0 + ) + null + else + totalDistance, + "totalDistanceUnit" to + "METER", + "totalEnergyBurned" to + if (totalEnergyBurned == + 0.0 + ) + null + else + totalEnergyBurned, + "totalEnergyBurnedUnit" to + "KILOCALORIE", + "totalSteps" to + if (totalSteps == + 0.0 + ) + null + else + totalSteps, + "totalStepsUnit" to + "COUNT", + "unit" to "MINUTES", + "date_from" to + rec.startTime + .toEpochMilli(), + "date_to" to + rec.endTime.toEpochMilli(), + "source_id" to "", + "source_name" to + record.metadata + .dataOrigin + .packageName, + ), + ) + } + // Filter sleep stages for requested stage + } else if (classType == SleepSessionRecord::class) { + for (rec in response.records) { + if (rec is SleepSessionRecord) { + if (dataType == SLEEP_SESSION) { + healthConnectData.addAll( + convertRecord( + rec, + dataType + ) + ) + } else { + for (recStage in rec.stages) { + if (dataType == + MapSleepStageToType[ + recStage.stage] + ) { + healthConnectData + .addAll( + convertRecordStage( + recStage, + dataType, + rec.metadata.dataOrigin + .packageName + ) + ) + } } } } } - } - } else { - for (rec in records) { - healthConnectData.addAll( - convertRecord(rec, dataType) - ) + } else { + for (rec in records) { + healthConnectData.addAll( + convertRecord(rec, dataType) + ) + } } } + Handler(context!!.mainLooper).run { result.success(healthConnectData) } + } catch (e: Exception) { + Log.i("FLUTTER_HEALTH::ERROR", "Unable to return $dataType due to the following exception:") + Log.e("FLUTTER_HEALTH::ERROR", Log.getStackTraceString(e)) + result.success(null) } - Handler(context!!.mainLooper).run { result.success(healthConnectData) } } } @@ -2862,61 +2869,67 @@ class HealthPlugin(private var channel: MethodChannel? = null) : val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) val healthConnectData = mutableListOf>() scope.launch { - MapToHCAggregateMetric[dataType]?.let { metricClassType -> - val request = - AggregateGroupByDurationRequest( - metrics = setOf(metricClassType), - timeRangeFilter = - TimeRangeFilter.between( - startTime, - endTime - ), - timeRangeSlicer = - Duration.ofSeconds( - interval - ) - ) - val response = healthConnectClient.aggregateGroupByDuration(request) - - for (durationResult in response) { - // The result may be null if no data is available in the - // time range - var totalValue = durationResult.result[metricClassType] - if (totalValue is Length) { - totalValue = totalValue.inMeters - } else if (totalValue is Energy) { - totalValue = totalValue.inKilocalories - } - - val packageNames = - durationResult.result.dataOrigins - .joinToString { origin -> - "${origin.packageName}" - } - - val data = - mapOf( - "value" to - (totalValue - ?: 0), - "date_from" to - durationResult.startTime - .toEpochMilli(), - "date_to" to - durationResult.endTime - .toEpochMilli(), - "source_name" to - packageNames, - "source_id" to "", - "is_manual_entry" to - packageNames.contains( - "user_input" + try { + MapToHCAggregateMetric[dataType]?.let { metricClassType -> + val request = + AggregateGroupByDurationRequest( + metrics = setOf(metricClassType), + timeRangeFilter = + TimeRangeFilter.between( + startTime, + endTime + ), + timeRangeSlicer = + Duration.ofSeconds( + interval ) ) - healthConnectData.add(data) + val response = healthConnectClient.aggregateGroupByDuration(request) + + for (durationResult in response) { + // The result may be null if no data is available in the + // time range + var totalValue = durationResult.result[metricClassType] + if (totalValue is Length) { + totalValue = totalValue.inMeters + } else if (totalValue is Energy) { + totalValue = totalValue.inKilocalories + } + + val packageNames = + durationResult.result.dataOrigins + .joinToString { origin -> + "${origin.packageName}" + } + + val data = + mapOf( + "value" to + (totalValue + ?: 0), + "date_from" to + durationResult.startTime + .toEpochMilli(), + "date_to" to + durationResult.endTime + .toEpochMilli(), + "source_name" to + packageNames, + "source_id" to "", + "is_manual_entry" to + packageNames.contains( + "user_input" + ) + ) + healthConnectData.add(data) + } } + Handler(context!!.mainLooper).run { result.success(healthConnectData) } + } catch (e: Exception) { + Log.i("FLUTTER_HEALTH::ERROR", "Unable to return $dataType due to the following exception:") + Log.e("FLUTTER_HEALTH::ERROR", Log.getStackTraceString(e)) + result.success(null) } - Handler(context!!.mainLooper).run { result.success(healthConnectData) } } } From 8079eafc1653651eca277e25fe8bc2c3e035700a Mon Sep 17 00:00:00 2001 From: Watcharachai Kanjaikaew Date: Thu, 25 Jul 2024 22:48:50 +0700 Subject: [PATCH 08/80] feat: manual entry filter for getTotalStepsInInterval (#980) --- .../cachet/plugins/health/HealthPlugin.kt | 74 ++++++++++++------- .../ios/Classes/SwiftHealthPlugin.swift | 7 +- packages/health/lib/src/health_plugin.dart | 6 +- 3 files changed, 59 insertions(+), 28 deletions(-) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index eba81d4af..5ec9460f2 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -2230,6 +2230,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : private fun getTotalStepsInInterval(call: MethodCall, result: Result) { val start = call.argument("startTime")!! val end = call.argument("endTime")!! + val includeManualEntry = call.argument("includeManualEntry")!! if (useHealthConnectIfAvailable && healthConnectAvailable) { getStepsHealthConnect(start, end, result) @@ -2260,7 +2261,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : val request = DataReadRequest.Builder() - .aggregate(ds) + .read(ds) .bucketByTime(duration, TimeUnit.MILLISECONDS) .setTimeRange(start, end, TimeUnit.MILLISECONDS) .build() @@ -2278,7 +2279,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : getStepsInRange( start, end, - aggregatedDataType, + includeManualEntry, result ), ) @@ -2320,36 +2321,57 @@ class HealthPlugin(private var channel: MethodChannel? = null) : } private fun getStepsInRange( - start: Long, - end: Long, - aggregatedDataType: DataType, - result: Result, - ) = OnSuccessListener { response: DataReadResponse -> - val map = HashMap() // need to return to Dart so can't use sparse array + start: Long, + end: Long, + includeManualEntry: Boolean, + result: Result + ) = OnSuccessListener { response: DataReadResponse -> + var totalSteps = 0 // Variable to accumulate the total steps. + for (bucket in response.buckets) { - val dp = bucket.dataSets.firstOrNull()?.dataPoints?.firstOrNull() - if (dp != null) { - val count = dp.getValue(aggregatedDataType.fields[0]) + for (dataSet in bucket.dataSets) { + var dataPoints = dataSet.dataPoints + if (!includeManualEntry) { + dataPoints = + dataPoints.filterIndexed { _, dataPoint -> + !dataPoint.originalDataSource + .streamName + .contains( + "user_input" + ) + } + } + for (dp in dataPoints) { + val streamName = dp.originalDataSource.streamName + if (!includeManualEntry && streamName.contains("user_input")) { + // Skip this data point if manual entry is not included + Log.i("FLUTTER_HEALTH::SKIPPED", "Skipping manual entry data point with stream name $streamName") + continue + } - val startTime = dp.getStartTime(TimeUnit.MILLISECONDS) - val startDate = Date(startTime) - val endDate = Date(dp.getEndTime(TimeUnit.MILLISECONDS)) - Log.i( - "FLUTTER_HEALTH::SUCCESS", - "returning $count steps for $startDate - $endDate", - ) - map[startTime] = count.asInt() - } else { - val startDay = Date(start) - val endDay = Date(end) - Log.i("FLUTTER_HEALTH::ERROR", "no steps for $startDay - $endDay") + val count = dp.getValue(Field.FIELD_STEPS) + totalSteps += count.asInt() + + val startTime = dp.getStartTime(TimeUnit.MILLISECONDS) + val startDate = Date(startTime) + val endDate = Date(dp.getEndTime(TimeUnit.MILLISECONDS)) + Log.i( + "FLUTTER_HEALTH::INFO", + "adding $count steps for $startDate - $endDate. Total so far: $totalSteps", + ) + } } } - assert(map.size <= 1) { - "getTotalStepsInInterval should return only one interval. Found: ${map.size}" + if (totalSteps == 0) { + val startDay = Date(start) + val endDay = Date(end) + Log.i("FLUTTER_HEALTH::ERROR", "no steps for $startDay - $endDay") } - Handler(context!!.mainLooper).run { result.success(map.values.firstOrNull()) } + + Log.i("FLUTTER_HEALTH::SUCCESS", "Final total steps in interval: $totalSteps") + + Handler(context!!.mainLooper).run { result.success(totalSteps) } } /// Disconnect Google fit diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index efc83f371..6c7e3a0b0 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -945,14 +945,19 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let arguments = call.arguments as? NSDictionary let startTime = (arguments?["startTime"] as? NSNumber) ?? 0 let endTime = (arguments?["endTime"] as? NSNumber) ?? 0 + let includeManualEntry = (arguments?["includeManualEntry"] as? Bool) ?? true // Convert dates from milliseconds to Date() let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) let sampleType = HKQuantityType.quantityType(forIdentifier: .stepCount)! - let predicate = HKQuery.predicateForSamples( + var predicate = HKQuery.predicateForSamples( withStart: dateFrom, end: dateTo, options: .strictStartDate) + if (!includeManualEntry) { + let manualPredicate = NSPredicate(format: "metadata.%K != YES", HKMetadataKeyWasUserEntered) + predicate = NSCompoundPredicate(type: .and, subpredicates: [predicate, manualPredicate]) + } let query = HKStatisticsQuery( quantityType: sampleType, diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 1d22dd68e..f7a647b8f 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -841,10 +841,14 @@ class Health { Future getTotalStepsInInterval( DateTime startTime, DateTime endTime, + { + bool includeManualEntry = true + } ) async { final args = { 'startTime': startTime.millisecondsSinceEpoch, - 'endTime': endTime.millisecondsSinceEpoch + 'endTime': endTime.millisecondsSinceEpoch, + 'includeManualEntry': includeManualEntry }; final stepsCount = await _channel.invokeMethod( 'getTotalStepsInInterval', From 0bc7bfb2d852e8edae2a455b6f7e5aec32bc4adb Mon Sep 17 00:00:00 2001 From: chuanpham <50976620+chuanpham@users.noreply.github.com> Date: Thu, 25 Jul 2024 23:14:51 +0700 Subject: [PATCH 09/80] [Android] Health Connect: Add revokeAllPermissions() (#962) * revokeAllPermissions revokeAllPermissions * Update documentation, remove a commented line * Update example app's permission revoking UI --------- Co-authored-by: Aamir Farooq --- .../cachet/plugins/health/HealthPlugin.kt | 11 +++++-- packages/health/example/lib/main.dart | 33 +++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 5ec9460f2..3cbc5e3f9 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -2194,15 +2194,20 @@ class HealthPlugin(private var channel: MethodChannel? = null) : } /** - * Revokes access to Google Fit using the `disableFit`-method. + * Revokes access to Health Connect using `revokeAllPermissions` and Google Fit using the `disableFit`-method. * * Note: Using the `revokeAccess` creates a bug on android when trying to reapply for * permissions afterwards, hence `disableFit` was used. + * Note: When using `revokePermissions` with Health Connect, the app must be completely killed + * for it to take effect. */ private fun revokePermissions(call: MethodCall, result: Result) { if (useHealthConnectIfAvailable && healthConnectAvailable) { - result.notImplemented() - return + scope.launch { + Log.i("Health", "Disabling Health Connect") + healthConnectClient.permissionController.revokeAllPermissions() + } + result.success(true) } if (context == null) { result.success(false) diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index 2e77ab376..412099989 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -27,6 +27,9 @@ enum AppState { DATA_NOT_DELETED, STEPS_READY, HEALTH_CONNECT_STATUS, + PERMISSIONS_REVOKING, + PERMISSIONS_REVOKED, + PERMISSIONS_NOT_REVOKED, } class _HealthAppState extends State { @@ -347,11 +350,21 @@ class _HealthAppState extends State { /// Revoke access to health data. Note, this only has an effect on Android. Future revokeAccess() async { + setState(() => _state = AppState.PERMISSIONS_REVOKING); + + bool success = false; try { await Health().revokePermissions(); + success = true; } catch (error) { debugPrint("Exception in revokeAccess: $error"); } + + setState(() { + _state = success + ? AppState.PERMISSIONS_REVOKED + : AppState.PERMISSIONS_NOT_REVOKED; + }); } // UI building below @@ -438,6 +451,23 @@ class _HealthAppState extends State { ); } + Widget get _permissionsRevoking => Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + padding: EdgeInsets.all(20), + child: CircularProgressIndicator( + strokeWidth: 10, + )), + Text('Revoking permissions...') + ], + ); + + Widget get _permissionsRevoked => const Text('Permissions revoked.'); + + Widget get _permissionsNotRevoked => + const Text('Failed to revoke permissions'); + Widget get _contentFetchingData => Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -536,5 +566,8 @@ class _HealthAppState extends State { AppState.DATA_NOT_DELETED => _dataNotDeleted, AppState.STEPS_READY => _stepsFetched, AppState.HEALTH_CONNECT_STATUS => _contentHealthConnectStatus, + AppState.PERMISSIONS_REVOKING => _permissionsRevoking, + AppState.PERMISSIONS_REVOKED => _permissionsRevoked, + AppState.PERMISSIONS_NOT_REVOKED => _permissionsNotRevoked, }; } From dc2eed87062d3288f2f557327e4abaf8e1f6efec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Valente?= Date: Fri, 26 Jul 2024 11:03:10 +0100 Subject: [PATCH 10/80] Add insulinDelivery support for iOS (#675) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add insulinDelivery support for iOS * add insulin delivery reason metadata * add metadata to health data point * fix insulin delivery health value from json * allow to write insulin delivery to apple health * check if insulin delivery reason is valid * fix crash * fix crash * ignore meal name * update deps * revert update * fix write meal crash * bump device info plus dep * add missing data type for ios * add docs * fix merge typo * temp fix for dexcom crash * update code * update code * get insulin delivery reason from apple health * fix metadata parsing * fix crash with older devices * let write meal even tho there's no nutrition value i.e. water (nutrition allways needs to be set to prevent crash) * Throw exception when `writeInsulinDelivery` is called on Android --------- Co-authored-by: António Valente Co-authored-by: Aamir Farooq --- packages/health/README.md | 1 + .../ios/Classes/SwiftHealthPlugin.swift | 81 ++++++++++++------- packages/health/lib/health.g.dart | 38 ++++++++- .../health/lib/src/health_data_point.dart | 18 ++++- packages/health/lib/src/health_plugin.dart | 45 ++++++++++- .../health/lib/src/health_value_types.dart | 49 +++++++++++ packages/health/lib/src/heath_data_types.dart | 10 +++ 7 files changed, 207 insertions(+), 35 deletions(-) diff --git a/packages/health/README.md b/packages/health/README.md index 30f87c6ca..bc7d1b474 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -348,6 +348,7 @@ The plugin supports the following [`HealthDataType`](https://pub.dev/documentati | AUDIOGRAM | DECIBEL_HEARING_LEVEL | yes | | | | | ELECTROCARDIOGRAM | VOLT | yes | | | Requires Apple Watch to write the data | | NUTRITION | NO_UNIT | yes | yes | yes | | +| INSULIN_DELIVERY | INTERNATIONAL_UNIT | yes | | | | ## Workout Types diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 6c7e3a0b0..9f8ba3ba2 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -36,6 +36,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let HEART_RATE = "HEART_RATE" let HEART_RATE_VARIABILITY_SDNN = "HEART_RATE_VARIABILITY_SDNN" let HEIGHT = "HEIGHT" + let INSULIN_DELIVERY = "INSULIN_DELIVERY" let HIGH_HEART_RATE_EVENT = "HIGH_HEART_RATE_EVENT" let IRREGULAR_HEART_RATE_EVENT = "IRREGULAR_HEART_RATE_EVENT" let LOW_HEART_RATE_EVENT = "LOW_HEART_RATE_EVENT" @@ -181,6 +182,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { try! writeMeal(call: call, result: result) } + /// Handle writeInsulinDelivery + else if (call.method.elementsEqual("writeInsulinDelivery")){ + try! writeInsulinDelivery(call: call, result: result) + } + /// Handle writeWorkoutData else if call.method.elementsEqual("writeWorkoutData") { try! writeWorkoutData(call: call, result: result) @@ -456,6 +462,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments") } + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) @@ -468,33 +475,24 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { var nutrition = Set() - if(calories > 0) { - let caloriesSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryEnergyConsumed)!, quantity: HKQuantity(unit: HKUnit.kilocalorie(), doubleValue: calories), start: dateFrom, end: dateTo, metadata: metadata) - nutrition.insert(caloriesSample) - } - - if(carbs > 0) { - let carbsSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryCarbohydrates)!, quantity: HKQuantity(unit: HKUnit.gram(), doubleValue: carbs), start: dateFrom, end: dateTo, metadata: metadata) - nutrition.insert(carbsSample) - } - - if(protein > 0) { - let proteinSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryProtein)!, quantity: HKQuantity(unit: HKUnit.gram(), doubleValue: protein), start: dateFrom, end: dateTo, metadata: metadata) - nutrition.insert(proteinSample) - } - - if(fat > 0) { - let fatSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryFatTotal)!, quantity: HKQuantity(unit: HKUnit.gram(), doubleValue: fat), start: dateFrom, end: dateTo, metadata: metadata) - nutrition.insert(fatSample) - } - - if(caffeine > 0) { - let caffeineSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryCaffeine)!, quantity: HKQuantity(unit: HKUnit.gram(), doubleValue: caffeine), start: dateFrom, end: dateTo, metadata: metadata) - nutrition.insert(caffeineSample) - } + let caloriesSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryEnergyConsumed)!, quantity: HKQuantity(unit: HKUnit.kilocalorie(), doubleValue: calories), start: dateFrom, end: dateTo, metadata: metadata) + nutrition.insert(caloriesSample) + + let carbsSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryCarbohydrates)!, quantity: HKQuantity(unit: HKUnit.gram(), doubleValue: carbs), start: dateFrom, end: dateTo, metadata: metadata) + nutrition.insert(carbsSample) + + let proteinSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryProtein)!, quantity: HKQuantity(unit: HKUnit.gram(), doubleValue: protein), start: dateFrom, end: dateTo, metadata: metadata) + nutrition.insert(proteinSample) + + let fatSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryFatTotal)!, quantity: HKQuantity(unit: HKUnit.gram(), doubleValue: fat), start: dateFrom, end: dateTo, metadata: metadata) + nutrition.insert(fatSample) + + let caffeineSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryCaffeine)!, quantity: HKQuantity(unit: HKUnit.gram(), doubleValue: caffeine), start: dateFrom, end: dateTo, metadata: metadata) + nutrition.insert(caffeineSample) if #available(iOS 15.0, *){ - let meal = HKCorrelation.init(type: HKCorrelationType.init(HKCorrelationTypeIdentifier.food), start: dateFrom, end: dateTo, objects: nutrition, metadata: metadata) + let type = HKCorrelationType.correlationType(forIdentifier: HKCorrelationTypeIdentifier.food)! + let meal = HKCorrelation(type: type, start: dateFrom, end: dateTo, objects: nutrition, metadata: metadata) HKHealthStore().save(meal, withCompletion: { (success, error) in if let err = error { @@ -508,7 +506,34 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { result(false) } } + func writeInsulinDelivery(call: FlutterMethodCall, result: @escaping FlutterResult) throws { + guard let arguments = call.arguments as? NSDictionary, + let units = (arguments["units"] as? Double), + let reason = (arguments["reason"] as? NSNumber), + let startTime = (arguments["startTime"] as? NSNumber), + let endTime = (arguments["endTime"] as? NSNumber) + else { + throw PluginError(message: "Invalid Arguments") + } + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) + let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) + let type = HKSampleType.quantityType(forIdentifier: .insulinDelivery)! + let quantity = HKQuantity(unit: HKUnit.internationalUnit(), doubleValue: units) + let metadata = [HKMetadataKeyInsulinDeliveryReason: reason] + + let insulin_sample = HKQuantitySample(type: type, quantity: quantity, start: dateFrom, end: dateTo, metadata: metadata) + + HKHealthStore().save(insulin_sample, withCompletion: { (success, error) in + if let err = error { + print("Error Saving Insulin Delivery Sample: \(err.localizedDescription)") + } + DispatchQueue.main.async { + result(success) + } + }) + } + func writeWorkoutData(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let activityType = (arguments["activityType"] as? String), @@ -632,12 +657,13 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let dictionaries = samples.map { sample -> NSDictionary in return [ "uuid": "\(sample.uuid)", - "value": sample.quantity.doubleValue(for: unit!), + "value": sample.quantity.doubleValue(for: unit ?? HKUnit.internationalUnit()), "date_from": Int(sample.startDate.timeIntervalSince1970 * 1000), "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), "source_id": sample.sourceRevision.source.bundleIdentifier, "source_name": sample.sourceRevision.source.name, - "is_manual_entry": sample.metadata?[HKMetadataKeyWasUserEntered] != nil + "is_manual_entry": sample.metadata?[HKMetadataKeyWasUserEntered] != nil, + "metadata": dataTypeKey == INSULIN_DELIVERY ? sample.metadata : nil ] } DispatchQueue.main.async { @@ -1176,6 +1202,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataTypesDict[HEART_RATE_VARIABILITY_SDNN] = HKSampleType.quantityType( forIdentifier: .heartRateVariabilitySDNN)! dataTypesDict[HEIGHT] = HKSampleType.quantityType(forIdentifier: .height)! + dataTypesDict[INSULIN_DELIVERY] = HKSampleType.quantityType(forIdentifier: .insulinDelivery)! dataTypesDict[RESTING_HEART_RATE] = HKSampleType.quantityType( forIdentifier: .restingHeartRate)! dataTypesDict[STEPS] = HKSampleType.quantityType(forIdentifier: .stepCount)! diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart index 8b23ccc0e..43941092d 100644 --- a/packages/health/lib/health.g.dart +++ b/packages/health/lib/health.g.dart @@ -23,6 +23,7 @@ HealthDataPoint _$HealthDataPointFromJson(Map json) => ? null : WorkoutSummary.fromJson( json['workout_summary'] as Map), + metadata: json['metadata'] as Map?, ); Map _$HealthDataPointToJson(HealthDataPoint instance) { @@ -46,6 +47,7 @@ Map _$HealthDataPointToJson(HealthDataPoint instance) { } writeNotNull('workout_summary', instance.workoutSummary); + writeNotNull('metadata', instance.metadata); return val; } @@ -70,6 +72,7 @@ const _$HealthDataTypeEnumMap = { HealthDataType.HEART_RATE: 'HEART_RATE', HealthDataType.HEART_RATE_VARIABILITY_SDNN: 'HEART_RATE_VARIABILITY_SDNN', HealthDataType.HEIGHT: 'HEIGHT', + HealthDataType.INSULIN_DELIVERY: 'INSULIN_DELIVERY', HealthDataType.RESTING_HEART_RATE: 'RESTING_HEART_RATE', HealthDataType.RESPIRATORY_RATE: 'RESPIRATORY_RATE', HealthDataType.PERIPHERAL_PERFUSION_INDEX: 'PERIPHERAL_PERFUSION_INDEX', @@ -239,13 +242,13 @@ WorkoutHealthValue _$WorkoutHealthValueFromJson(Map json) => WorkoutHealthValue( workoutActivityType: $enumDecode( _$HealthWorkoutActivityTypeEnumMap, json['workout_activity_type']), - totalEnergyBurned: json['total_energy_burned'] as int?, + totalEnergyBurned: (json['total_energy_burned'] as num?)?.toInt(), totalEnergyBurnedUnit: $enumDecodeNullable( _$HealthDataUnitEnumMap, json['total_energy_burned_unit']), - totalDistance: json['total_distance'] as int?, + totalDistance: (json['total_distance'] as num?)?.toInt(), totalDistanceUnit: $enumDecodeNullable( _$HealthDataUnitEnumMap, json['total_distance_unit']), - totalSteps: json['total_steps'] as int?, + totalSteps: (json['total_steps'] as num?)?.toInt(), totalStepsUnit: $enumDecodeNullable( _$HealthDataUnitEnumMap, json['total_steps_unit']), )..$type = json['__type'] as String?; @@ -497,6 +500,35 @@ Map _$ElectrocardiogramVoltageValueToJson( return val; } +InsulinDeliveryHealthValue _$InsulinDeliveryHealthValueFromJson( + Map json) => + InsulinDeliveryHealthValue( + units: (json['units'] as num).toDouble(), + reason: $enumDecode(_$InsulinDeliveryReasonEnumMap, json['reason']), + )..$type = json['__type'] as String?; + +Map _$InsulinDeliveryHealthValueToJson( + InsulinDeliveryHealthValue instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('__type', instance.$type); + val['units'] = instance.units; + val['reason'] = _$InsulinDeliveryReasonEnumMap[instance.reason]!; + return val; +} + +const _$InsulinDeliveryReasonEnumMap = { + InsulinDeliveryReason.NOT_SET: 'NOT_SET', + InsulinDeliveryReason.BASAL: 'BASAL', + InsulinDeliveryReason.BOLUS: 'BOLUS', +}; + NutritionHealthValue _$NutritionHealthValueFromJson( Map json) => NutritionHealthValue( diff --git a/packages/health/lib/src/health_data_point.dart b/packages/health/lib/src/health_data_point.dart index 3a6f65f95..0c971bf1d 100644 --- a/packages/health/lib/src/health_data_point.dart +++ b/packages/health/lib/src/health_data_point.dart @@ -47,6 +47,9 @@ class HealthDataPoint { /// The summary of the workout data point, if available. WorkoutSummary? workoutSummary; + /// The metadata for this data point. + Map? metadata; + HealthDataPoint({ required this.value, required this.type, @@ -59,6 +62,7 @@ class HealthDataPoint { required this.sourceName, this.isManualEntry = false, this.workoutSummary, + this.metadata, }) { // set the value to minutes rather than the category // returned by the native API @@ -107,6 +111,8 @@ class HealthDataPoint { ElectrocardiogramHealthValue.fromHealthDataPoint(dataPoint), HealthDataType.NUTRITION => NutritionHealthValue.fromHealthDataPoint(dataPoint), + HealthDataType.INSULIN_DELIVERY => + InsulinDeliveryHealthValue.fromHealthDataPoint(dataPoint), _ => NumericHealthValue.fromHealthDataPoint(dataPoint), }; @@ -117,6 +123,9 @@ class HealthDataPoint { final String sourceId = dataPoint["source_id"] as String; final String sourceName = dataPoint["source_name"] as String; final bool isManualEntry = dataPoint["is_manual_entry"] as bool? ?? false; + final Map? metadata = dataPoint["metadata"] == null + ? null + : Map.from(dataPoint['metadata'] as Map); final unit = dataTypeToUnit[dataType] ?? HealthDataUnit.UNKNOWN_UNIT; // Set WorkoutSummary, if available. @@ -140,6 +149,7 @@ class HealthDataPoint { sourceName: sourceName, isManualEntry: isManualEntry, workoutSummary: workoutSummary, + metadata: metadata, ); } @@ -155,7 +165,8 @@ class HealthDataPoint { sourceId: $sourceId, sourceName: $sourceName isManualEntry: $isManualEntry - workoutSummary: $workoutSummary"""; + workoutSummary: $workoutSummary + metadata: $metadata"""; @override bool operator ==(Object other) => @@ -169,9 +180,10 @@ class HealthDataPoint { sourceDeviceId == other.sourceDeviceId && sourceId == other.sourceId && sourceName == other.sourceName && - isManualEntry == other.isManualEntry; + isManualEntry == other.isManualEntry && + metadata == other.metadata; @override int get hashCode => Object.hash(value, unit, dateFrom, dateTo, type, - sourcePlatform, sourceDeviceId, sourceId, sourceName); + sourcePlatform, sourceDeviceId, sourceId, sourceName, metadata); } diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index f7a647b8f..f72c6cacc 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -19,8 +19,8 @@ part of '../health.dart'; /// and [getHealthAggregateDataFromTypes] methods. /// * Reading total step counts using the [getTotalStepsInInterval] method. /// * Writing different types of specialized health data like the [writeWorkoutData], -/// [writeBloodPressure], [writeBloodOxygen], [writeAudiogram], and [writeMeal] -/// methods. +/// [writeBloodPressure], [writeBloodOxygen], [writeAudiogram], [writeMeal], +/// [writeInsulinDelivery] methods. class Health { static const MethodChannel _channel = MethodChannel('flutter_health'); static final _instance = Health._(); @@ -607,6 +607,47 @@ class Health { return await _channel.invokeMethod('writeAudiogram', args) == true; } + /// Saves insulin delivery record into Apple Health. + /// + /// Returns true if successful, false otherwise. + /// + /// Parameters: + /// * [units] - the number of units of insulin taken. + /// * [reason] - the insulin reason, basal or bolus. + /// * [startTime] - the start time when the meal was consumed. + /// It must be equal to or earlier than [endTime]. + /// * [endTime] - the end time when the meal was consumed. + /// It must be equal to or later than [startTime]. + Future writeInsulinDelivery( + double units, + InsulinDeliveryReason reason, + DateTime startTime, + DateTime endTime, + ) async { + if (startTime.isAfter(endTime)) { + throw ArgumentError("startTime must be equal or earlier than endTime"); + } + + if (reason == InsulinDeliveryReason.NOT_SET) { + throw ArgumentError("set a valid insulin delivery reason"); + } + + if (Platform.isAndroid) { + throw UnsupportedError( + "writeInsulinDelivery is not supported on Android"); + } + + Map args = { + 'units': units, + 'reason': reason.index, + 'startTime': startTime.millisecondsSinceEpoch, + 'endTime': endTime.millisecondsSinceEpoch + }; + + bool? success = await _channel.invokeMethod('writeInsulinDelivery', args); + return success ?? false; + } + /// Fetch a list of health data points based on [types]. Future> getHealthDataFromTypes({ required List types, diff --git a/packages/health/lib/src/health_value_types.dart b/packages/health/lib/src/health_value_types.dart index ad5fdecbb..1e646f2b1 100644 --- a/packages/health/lib/src/health_value_types.dart +++ b/packages/health/lib/src/health_value_types.dart @@ -321,6 +321,55 @@ class ElectrocardiogramVoltageValue extends HealthValue { String toString() => '$runtimeType - voltage: $voltage'; } +/// A [HealthValue] object from insulin delivery (iOS only) +@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) +class InsulinDeliveryHealthValue extends HealthValue { + /// The amount of units of insulin taken + double units; + + /// If it's basal, bolus or unknown reason for insulin dosage + InsulinDeliveryReason reason; + + InsulinDeliveryHealthValue({ + required this.units, + required this.reason, + }); + + factory InsulinDeliveryHealthValue.fromHealthDataPoint(dynamic dataPoint) { + final units = dataPoint['value'] as num; + + final metadata = dataPoint['metadata'] == null + ? null + : Map.from(dataPoint['metadata'] as Map); + final reasonIndex = + metadata == null || !metadata.containsKey('HKInsulinDeliveryReason') + ? 0 + : metadata['HKInsulinDeliveryReason'] as double; + final reason = InsulinDeliveryReason.values[reasonIndex.toInt()]; + + return InsulinDeliveryHealthValue(units: units.toDouble(), reason: reason); + } + + @override + Function get fromJsonFunction => _$InsulinDeliveryHealthValueFromJson; + factory InsulinDeliveryHealthValue.fromJson(Map json) => + FromJsonFactory().fromJson(json) as InsulinDeliveryHealthValue; + @override + Map toJson() => _$InsulinDeliveryHealthValueToJson(this); + + @override + bool operator ==(Object other) => + other is InsulinDeliveryHealthValue && + units == other.units && + reason == other.reason; + + @override + int get hashCode => Object.hash(units, reason); + + @override + String toString() => '$runtimeType - units: $units, reason: $reason'; +} + /// A [HealthValue] object for nutrition. /// /// Parameters: diff --git a/packages/health/lib/src/heath_data_types.dart b/packages/health/lib/src/heath_data_types.dart index dfd35efce..28caa58f0 100644 --- a/packages/health/lib/src/heath_data_types.dart +++ b/packages/health/lib/src/heath_data_types.dart @@ -22,6 +22,7 @@ enum HealthDataType { HEART_RATE, HEART_RATE_VARIABILITY_SDNN, HEIGHT, + INSULIN_DELIVERY, RESTING_HEART_RATE, RESPIRATORY_RATE, PERIPHERAL_PERFUSION_INDEX, @@ -97,6 +98,7 @@ const List dataTypeKeysIOS = [ HealthDataType.HEART_RATE, HealthDataType.HEART_RATE_VARIABILITY_SDNN, HealthDataType.HEIGHT, + HealthDataType.INSULIN_DELIVERY, HealthDataType.HIGH_HEART_RATE_EVENT, HealthDataType.IRREGULAR_HEART_RATE_EVENT, HealthDataType.LOW_HEART_RATE_EVENT, @@ -191,6 +193,7 @@ const Map dataTypeToUnit = { HealthDataType.RESPIRATORY_RATE: HealthDataUnit.RESPIRATIONS_PER_MINUTE, HealthDataType.PERIPHERAL_PERFUSION_INDEX: HealthDataUnit.PERCENT, HealthDataType.HEIGHT: HealthDataUnit.METER, + HealthDataType.INSULIN_DELIVERY: HealthDataUnit.INTERNATIONAL_UNIT, HealthDataType.RESTING_HEART_RATE: HealthDataUnit.BEATS_PER_MINUTE, HealthDataType.STEPS: HealthDataUnit.COUNT, HealthDataType.WAIST_CIRCUMFERENCE: HealthDataUnit.METER, @@ -505,6 +508,13 @@ enum ElectrocardiogramClassification { UNRECOGNIZED, } +/// Types of insulin delivery reason +enum InsulinDeliveryReason { + NOT_SET, + BASAL, + BOLUS, +} + /// Extension to assign numbers to [ElectrocardiogramClassification]s extension ElectrocardiogramClassificationValue on ElectrocardiogramClassification { From 8f744bd1f8fe06f1ca705454adb30b3881efcdcd Mon Sep 17 00:00:00 2001 From: David Date: Tue, 30 Jul 2024 06:50:37 -0400 Subject: [PATCH 11/80] [Health] Expand Micro Nutrient Support (#983) * feat: expanded NutritionHealthValue nutrients support Expanded NutritionHealthValue class to support more micronutrients. Not including nutrients that are neither supported in Health Kit or Health Connect. For example, Omega-3s, Omega-6, and amino acids feat: expanded nutrient support on the writeMeal method feat: Support writing micronutrients to platform Platforms do not support 100% of new micronutrients fix: NutrientRecords can now load foods that have null values feat: Read micronutrients from HC/GF chore: standardized writeMeal and getData->Nutrition data format fix: mealType was incorrect field key update snack writing example feat: added ios nutrient health data types feat: Add new dietary strings to ios plugin xcode project fix: swift code errors Trying to keep the data format consistent between serializing and deserializing remove my workspace files * Removed changes to Google Fit WriteMeal * Add missing documentation * Reorder lines in doc comments --------- Co-authored-by: Aamir Farooq --- packages/health/README.md | 402 +++++++++--------- .../cachet/plugins/health/HealthPlugin.kt | 167 ++++++-- packages/health/example/lib/main.dart | 36 ++ .../ios/Classes/SwiftHealthPlugin.swift | 328 +++++++++----- packages/health/lib/health.g.dart | 115 ++++- packages/health/lib/src/health_plugin.dart | 125 +++++- .../health/lib/src/health_value_types.dart | 375 ++++++++++++++-- packages/health/lib/src/heath_data_types.dart | 100 +++++ 8 files changed, 1263 insertions(+), 385 deletions(-) diff --git a/packages/health/README.md b/packages/health/README.md index bc7d1b474..8af88edeb 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -191,7 +191,7 @@ Below is a simplified flow of how to use the plugin. ```dart // configure the health plugin before use. Health().configure(useHealthConnectIfAvailable: true); - + // define the types to get var types = [ HealthDataType.STEPS, @@ -248,19 +248,19 @@ A `HealthDataPoint` object can be serialized to and from JSON using the `toJson( ```json { - "value": { - "__type": "NumericHealthValue", - "numeric_value": 141.0 - }, - "type": "STEPS", - "unit": "COUNT", - "date_from": "2024-04-03T10:06:57.736", - "date_to": "2024-04-03T10:12:51.724", - "source_platform": "appleHealth", - "source_device_id": "F74938B9-C011-4DE4-AA5E-CF41B60B96E7", - "source_id": "com.apple.health.81AE7156-EC05-47E3-AC93-2D6F65C717DF", - "source_name": "iPhone12.bardram.net", - "is_manual_entry": false + "value": { + "__type": "NumericHealthValue", + "numeric_value": 141.0 + }, + "type": "STEPS", + "unit": "COUNT", + "date_from": "2024-04-03T10:06:57.736", + "date_to": "2024-04-03T10:12:51.724", + "source_platform": "appleHealth", + "source_device_id": "F74938B9-C011-4DE4-AA5E-CF41B60B96E7", + "source_id": "com.apple.health.81AE7156-EC05-47E3-AC93-2D6F65C717DF", + "source_name": "iPhone12.bardram.net", + "is_manual_entry": false } ``` @@ -299,55 +299,55 @@ points = Health().removeDuplicates(points); The plugin supports the following [`HealthDataType`](https://pub.dev/documentation/health/latest/health/HealthDataType.html). | **Data Type** | **Unit** | **Apple Health** | **Google Fit** | **Google Health Connect** | **Comments** | -| --------------------------- | ----------------------- | ------- | ----------------------- |---------------------------| -------------------------------------- | -| ACTIVE_ENERGY_BURNED | CALORIES | yes | yes | yes | | -| BASAL_ENERGY_BURNED | CALORIES | yes | | yes | | -| BLOOD_GLUCOSE | MILLIGRAM_PER_DECILITER | yes | yes | yes | | -| BLOOD_OXYGEN | PERCENTAGE | yes | yes | yes | | -| BLOOD_PRESSURE_DIASTOLIC | MILLIMETER_OF_MERCURY | yes | yes | yes | | -| BLOOD_PRESSURE_SYSTOLIC | MILLIMETER_OF_MERCURY | yes | yes | yes | | -| BODY_FAT_PERCENTAGE | PERCENTAGE | yes | yes | yes | | -| BODY_MASS_INDEX | NO_UNIT | yes | yes | yes | | -| BODY_TEMPERATURE | DEGREE_CELSIUS | yes | yes | yes | | -| BODY_WATER_MASS | KILOGRAMS | | | yes | | -| ELECTRODERMAL_ACTIVITY | SIEMENS | yes | | | | -| HEART_RATE | BEATS_PER_MINUTE | yes | yes | yes | | -| HEIGHT | METERS | yes | yes | yes | | -| RESTING_HEART_RATE | BEATS_PER_MINUTE | yes | | yes | | -| RESPIRATORY_RATE | RESPIRATIONS_PER_MINUTE | yes | | yes | | -| PERIPHERAL_PERFUSION_INDEX | PERCENTAGE | yes | | | | -| STEPS | COUNT | yes | yes | yes | | -| WAIST_CIRCUMFERENCE | METERS | yes | | | | -| WALKING_HEART_RATE | BEATS_PER_MINUTE | yes | | | | -| WEIGHT | KILOGRAMS | yes | yes | yes | | -| DISTANCE_WALKING_RUNNING | METERS | yes | | | | -| FLIGHTS_CLIMBED | COUNT | yes | | yes | | -| MOVE_MINUTES | MINUTES | | yes | | | -| DISTANCE_DELTA | METERS | | yes | yes | | -| MINDFULNESS | MINUTES | yes | | | | -| SLEEP_IN_BED | MINUTES | yes | | | | -| SLEEP_ASLEEP | MINUTES | yes | | yes | | -| SLEEP_AWAKE | MINUTES | yes | | yes | | -| SLEEP_DEEP | MINUTES | yes | | yes | | -| SLEEP_LIGHT | MINUTES | | | yes | | -| SLEEP_REM | MINUTES | yes | | yes | | -| SLEEP_OUT_OF_BED | MINUTES | | | yes | | -| SLEEP_SESSION | MINUTES | | | yes | | -| WATER | LITER | yes | yes | yes | | -| EXERCISE_TIME | MINUTES | yes | | | | -| WORKOUT | NO_UNIT | yes | yes | yes | See table below | -| HIGH_HEART_RATE_EVENT | NO_UNIT | yes | | | Requires Apple Watch to write the data | -| LOW_HEART_RATE_EVENT | NO_UNIT | yes | | | Requires Apple Watch to write the data | -| IRREGULAR_HEART_RATE_EVENT | NO_UNIT | yes | | | Requires Apple Watch to write the data | -| HEART_RATE_VARIABILITY_SDNN | MILLISECONDS | yes | | | Requires Apple Watch to write the data | -| HEADACHE_NOT_PRESENT | MINUTES | yes | | | | -| HEADACHE_MILD | MINUTES | yes | | | | -| HEADACHE_MODERATE | MINUTES | yes | | | | -| HEADACHE_SEVERE | MINUTES | yes | | | | -| HEADACHE_UNSPECIFIED | MINUTES | yes | | | | -| AUDIOGRAM | DECIBEL_HEARING_LEVEL | yes | | | | -| ELECTROCARDIOGRAM | VOLT | yes | | | Requires Apple Watch to write the data | -| NUTRITION | NO_UNIT | yes | yes | yes | | +| --------------------------- | ----------------------- | ---------------- | -------------- | ------------------------- | -------------------------------------- | +| ACTIVE_ENERGY_BURNED | CALORIES | yes | yes | yes | | +| BASAL_ENERGY_BURNED | CALORIES | yes | | yes | | +| BLOOD_GLUCOSE | MILLIGRAM_PER_DECILITER | yes | yes | yes | | +| BLOOD_OXYGEN | PERCENTAGE | yes | yes | yes | | +| BLOOD_PRESSURE_DIASTOLIC | MILLIMETER_OF_MERCURY | yes | yes | yes | | +| BLOOD_PRESSURE_SYSTOLIC | MILLIMETER_OF_MERCURY | yes | yes | yes | | +| BODY_FAT_PERCENTAGE | PERCENTAGE | yes | yes | yes | | +| BODY_MASS_INDEX | NO_UNIT | yes | yes | yes | | +| BODY_TEMPERATURE | DEGREE_CELSIUS | yes | yes | yes | | +| BODY_WATER_MASS | KILOGRAMS | | | yes | | +| ELECTRODERMAL_ACTIVITY | SIEMENS | yes | | | | +| HEART_RATE | BEATS_PER_MINUTE | yes | yes | yes | | +| HEIGHT | METERS | yes | yes | yes | | +| RESTING_HEART_RATE | BEATS_PER_MINUTE | yes | | yes | | +| RESPIRATORY_RATE | RESPIRATIONS_PER_MINUTE | yes | | yes | | +| PERIPHERAL_PERFUSION_INDEX | PERCENTAGE | yes | | | | +| STEPS | COUNT | yes | yes | yes | | +| WAIST_CIRCUMFERENCE | METERS | yes | | | | +| WALKING_HEART_RATE | BEATS_PER_MINUTE | yes | | | | +| WEIGHT | KILOGRAMS | yes | yes | yes | | +| DISTANCE_WALKING_RUNNING | METERS | yes | | | | +| FLIGHTS_CLIMBED | COUNT | yes | | yes | | +| MOVE_MINUTES | MINUTES | | yes | | | +| DISTANCE_DELTA | METERS | | yes | yes | | +| MINDFULNESS | MINUTES | yes | | | | +| SLEEP_IN_BED | MINUTES | yes | | | | +| SLEEP_ASLEEP | MINUTES | yes | | yes | | +| SLEEP_AWAKE | MINUTES | yes | | yes | | +| SLEEP_DEEP | MINUTES | yes | | yes | | +| SLEEP_LIGHT | MINUTES | | | yes | | +| SLEEP_REM | MINUTES | yes | | yes | | +| SLEEP_OUT_OF_BED | MINUTES | | | yes | | +| SLEEP_SESSION | MINUTES | | | yes | | +| WATER | LITER | yes | yes | yes | | +| EXERCISE_TIME | MINUTES | yes | | | | +| WORKOUT | NO_UNIT | yes | yes | yes | See table below | +| HIGH_HEART_RATE_EVENT | NO_UNIT | yes | | | Requires Apple Watch to write the data | +| LOW_HEART_RATE_EVENT | NO_UNIT | yes | | | Requires Apple Watch to write the data | +| IRREGULAR_HEART_RATE_EVENT | NO_UNIT | yes | | | Requires Apple Watch to write the data | +| HEART_RATE_VARIABILITY_SDNN | MILLISECONDS | yes | | | Requires Apple Watch to write the data | +| HEADACHE_NOT_PRESENT | MINUTES | yes | | | | +| HEADACHE_MILD | MINUTES | yes | | | | +| HEADACHE_MODERATE | MINUTES | yes | | | | +| HEADACHE_SEVERE | MINUTES | yes | | | | +| HEADACHE_UNSPECIFIED | MINUTES | yes | | | | +| AUDIOGRAM | DECIBEL_HEARING_LEVEL | yes | | | | +| ELECTROCARDIOGRAM | VOLT | yes | | | Requires Apple Watch to write the data | +| NUTRITION | NO_UNIT | yes | yes | yes | | | INSULIN_DELIVERY | INTERNATIONAL_UNIT | yes | | | | ## Workout Types @@ -355,141 +355,141 @@ The plugin supports the following [`HealthDataType`](https://pub.dev/documentati The plugin supports the following [`HealthWorkoutActivityType`](https://pub.dev/documentation/health/latest/health/HealthWorkoutActivityType.html). | **Workout Type** | **Apple Health** | **Google Fit** | **Google Health Connect** | **Comments** | -| -------------------------------- | ------- | ----------------------- | ---------------------------- | ----------------------------------------------------------------- | -| ARCHERY | yes | yes | | | -| BADMINTON | yes | yes | yes | | -| BASEBALL | yes | yes | yes | | -| BASKETBALL | yes | yes | yes | | -| BIKING | yes | yes | yes | on iOS this is CYCLING, but name changed here to fit with Android | -| BOXING | yes | yes | yes | | -| CRICKET | yes | yes | yes | | -| CURLING | yes | yes | | | -| ELLIPTICAL | yes | yes | yes | | -| FENCING | yes | yes | yes | | -| AMERICAN_FOOTBALL | yes | yes | yes | | -| AUSTRALIAN_FOOTBALL | yes | yes | yes | | -| SOCCER | yes | yes | | | -| GOLF | yes | yes | yes | | -| GYMNASTICS | yes | yes | yes | | -| HANDBALL | yes | yes | yes | | -| HIGH_INTENSITY_INTERVAL_TRAINING | yes | yes | yes | | -| HIKING | yes | yes | yes | | -| HOCKEY | yes | yes | | | -| SKATING | yes | yes | yes | On iOS this is skating_sports | -| JUMP_ROPE | yes | yes | | | -| KICKBOXING | yes | yes | | | -| MARTIAL_ARTS | yes | yes | yes | | -| PILATES | yes | yes | yes | | -| RACQUETBALL | yes | yes | yes | | -| RUGBY | yes | yes | yes | | -| RUNNING | yes | yes | yes | | -| ROWING | yes | yes | yes | | -| SAILING | yes | yes | yes | | -| CROSS_COUNTRY_SKIING | yes | yes | | | -| DOWNHILL_SKIING | yes | yes | | | -| SNOWBOARDING | yes | yes | yes | | -| SOFTBALL | yes | yes | yes | | -| SQUASH | yes | yes | yes | | -| STAIR_CLIMBING | yes | yes | yes | | -| SWIMMING | yes | yes | | | -| TABLE_TENNIS | yes | yes | yes | | -| TENNIS | yes | yes | yes | | -| VOLLEYBALL | yes | yes | yes | | -| WALKING | yes | yes | yes | | -| WATER_POLO | yes | yes | yes | | -| YOGA | yes | yes | yes | | -| BOWLING | yes | | | | -| CROSS_TRAINING | yes | | | | -| TRACK_AND_FIELD | yes | | | | -| DISC_SPORTS | yes | | | | -| LACROSSE | yes | | | | -| PREPARATION_AND_RECOVERY | yes | | | | -| FLEXIBILITY | yes | | | | -| COOLDOWN | yes | | | | -| WHEELCHAIR_WALK_PACE | yes | | | | -| WHEELCHAIR_RUN_PACE | yes | | | | -| HAND_CYCLING | yes | | | | -| CORE_TRAINING | yes | | | | -| FUNCTIONAL_STRENGTH_TRAINING | yes | | | | -| TRADITIONAL_STRENGTH_TRAINING | yes | | | | -| MIXED_CARDIO | yes | | | | -| STAIRS | yes | | | | -| STEP_TRAINING | yes | | | | -| FITNESS_GAMING | yes | | | | -| BARRE | yes | | | | -| CARDIO_DANCE | yes | | | | -| SOCIAL_DANCE | yes | | | | -| MIND_AND_BODY | yes | | | | -| PICKLEBALL | yes | | | | -| CLIMBING | yes | | | | -| EQUESTRIAN_SPORTS | yes | | | | -| FISHING | yes | | | | -| HUNTING | yes | | | | -| PLAY | yes | | | | -| SNOW_SPORTS | yes | | | | -| PADDLE_SPORTS | yes | | | | -| SURFING_SPORTS | yes | | | | -| WATER_FITNESS | yes | | | | -| WATER_SPORTS | yes | | | | -| TAI_CHI | yes | | | | -| WRESTLING | yes | | | | -| AEROBICS | | yes | | | -| BIATHLON | | yes | | | -| CALISTHENICS | | yes | yes | | -| CIRCUIT_TRAINING | | yes | | | -| CROSS_FIT | | yes | | | -| DANCING | | yes | yes | | -| DIVING | | yes | | | -| ELEVATOR | | yes | | | -| ERGOMETER | | yes | | | -| ESCALATOR | | yes | | | -| FRISBEE_DISC | | yes | yes | | -| GARDENING | | yes | | | -| GUIDED_BREATHING | | yes | yes | | -| HORSEBACK_RIDING | | yes | | | -| HOUSEWORK | | yes | | | -| INTERVAL_TRAINING | | yes | | | -| IN_VEHICLE | | yes | | | -| KAYAKING | | yes | | | -| KETTLEBELL_TRAINING | | yes | | | -| KICK_SCOOTER | | yes | | | -| KITE_SURFING | | yes | | | -| MEDITATION | | yes | | | -| MIXED_MARTIAL_ARTS | | yes | | | -| P90X | | yes | | | -| PARAGLIDING | | yes | yes | | -| POLO | | yes | | | -| ROCK_CLIMBING | (yes) | yes | yes | on iOS this will be stored as CLIMBING | -| RUNNING_JOGGING | (yes) | yes | | on iOS this will be stored as RUNNING | -| RUNNING_SAND | (yes) | yes | | on iOS this will be stored as RUNNING | -| RUNNING_TREADMILL | (yes) | yes | yes | on iOS this will be stored as RUNNING | -| SCUBA_DIVING | | yes | yes | | -| SKATING_CROSS | (yes) | yes | | on iOS this will be stored as SKATING | -| SKATING_INDOOR | (yes) | yes | | on iOS this will be stored as SKATING | -| SKATING_INLINE | (yes) | yes | | on iOS this will be stored as SKATING | -| SKIING_BACK_COUNTRY | | yes | | | -| SKIING_KITE | | yes | | | -| SKIING_ROLLER | | yes | | | -| SLEDDING | | yes | | | -| STAIR_CLIMBING_MACHINE | | yes | yes | | -| STANDUP_PADDLEBOARDING | | yes | | | -| STILL | | yes | | | -| STRENGTH_TRAINING | | yes | yes | | -| SURFING | | yes | yes | | -| SWIMMING_OPEN_WATER | | yes | yes | | -| SWIMMING_POOL | | yes | yes | | -| TEAM_SPORTS | | yes | | | -| TILTING | | yes | | | -| TREADMILL | | yes | | | -| VOLLEYBALL_BEACH | | yes | | | -| VOLLEYBALL_INDOOR | | yes | | | -| WAKEBOARDING | | yes | | | -| WALKING_FITNESS | | yes | | | -| WALKING_NORDIC | | yes | | | -| WALKING_STROLLER | | yes | | | -| WALKING_TREADMILL | | yes | | | -| WEIGHTLIFTING | | yes | yes | | -| WHEELCHAIR | | yes | yes | | -| WINDSURFING | | yes | | | -| ZUMBA | | yes | | | -| OTHER | yes | yes | | | +| -------------------------------- | ---------------- | -------------- | ------------------------- | ----------------------------------------------------------------- | +| ARCHERY | yes | yes | | | +| BADMINTON | yes | yes | yes | | +| BASEBALL | yes | yes | yes | | +| BASKETBALL | yes | yes | yes | | +| BIKING | yes | yes | yes | on iOS this is CYCLING, but name changed here to fit with Android | +| BOXING | yes | yes | yes | | +| CRICKET | yes | yes | yes | | +| CURLING | yes | yes | | | +| ELLIPTICAL | yes | yes | yes | | +| FENCING | yes | yes | yes | | +| AMERICAN_FOOTBALL | yes | yes | yes | | +| AUSTRALIAN_FOOTBALL | yes | yes | yes | | +| SOCCER | yes | yes | | | +| GOLF | yes | yes | yes | | +| GYMNASTICS | yes | yes | yes | | +| HANDBALL | yes | yes | yes | | +| HIGH_INTENSITY_INTERVAL_TRAINING | yes | yes | yes | | +| HIKING | yes | yes | yes | | +| HOCKEY | yes | yes | | | +| SKATING | yes | yes | yes | On iOS this is skating_sports | +| JUMP_ROPE | yes | yes | | | +| KICKBOXING | yes | yes | | | +| MARTIAL_ARTS | yes | yes | yes | | +| PILATES | yes | yes | yes | | +| RACQUETBALL | yes | yes | yes | | +| RUGBY | yes | yes | yes | | +| RUNNING | yes | yes | yes | | +| ROWING | yes | yes | yes | | +| SAILING | yes | yes | yes | | +| CROSS_COUNTRY_SKIING | yes | yes | | | +| DOWNHILL_SKIING | yes | yes | | | +| SNOWBOARDING | yes | yes | yes | | +| SOFTBALL | yes | yes | yes | | +| SQUASH | yes | yes | yes | | +| STAIR_CLIMBING | yes | yes | yes | | +| SWIMMING | yes | yes | | | +| TABLE_TENNIS | yes | yes | yes | | +| TENNIS | yes | yes | yes | | +| VOLLEYBALL | yes | yes | yes | | +| WALKING | yes | yes | yes | | +| WATER_POLO | yes | yes | yes | | +| YOGA | yes | yes | yes | | +| BOWLING | yes | | | | +| CROSS_TRAINING | yes | | | | +| TRACK_AND_FIELD | yes | | | | +| DISC_SPORTS | yes | | | | +| LACROSSE | yes | | | | +| PREPARATION_AND_RECOVERY | yes | | | | +| FLEXIBILITY | yes | | | | +| COOLDOWN | yes | | | | +| WHEELCHAIR_WALK_PACE | yes | | | | +| WHEELCHAIR_RUN_PACE | yes | | | | +| HAND_CYCLING | yes | | | | +| CORE_TRAINING | yes | | | | +| FUNCTIONAL_STRENGTH_TRAINING | yes | | | | +| TRADITIONAL_STRENGTH_TRAINING | yes | | | | +| MIXED_CARDIO | yes | | | | +| STAIRS | yes | | | | +| STEP_TRAINING | yes | | | | +| FITNESS_GAMING | yes | | | | +| BARRE | yes | | | | +| CARDIO_DANCE | yes | | | | +| SOCIAL_DANCE | yes | | | | +| MIND_AND_BODY | yes | | | | +| PICKLEBALL | yes | | | | +| CLIMBING | yes | | | | +| EQUESTRIAN_SPORTS | yes | | | | +| FISHING | yes | | | | +| HUNTING | yes | | | | +| PLAY | yes | | | | +| SNOW_SPORTS | yes | | | | +| PADDLE_SPORTS | yes | | | | +| SURFING_SPORTS | yes | | | | +| WATER_FITNESS | yes | | | | +| WATER_SPORTS | yes | | | | +| TAI_CHI | yes | | | | +| WRESTLING | yes | | | | +| AEROBICS | | yes | | | +| BIATHLON | | yes | | | +| CALISTHENICS | | yes | yes | | +| CIRCUIT_TRAINING | | yes | | | +| CROSS_FIT | | yes | | | +| DANCING | | yes | yes | | +| DIVING | | yes | | | +| ELEVATOR | | yes | | | +| ERGOMETER | | yes | | | +| ESCALATOR | | yes | | | +| FRISBEE_DISC | | yes | yes | | +| GARDENING | | yes | | | +| GUIDED_BREATHING | | yes | yes | | +| HORSEBACK_RIDING | | yes | | | +| HOUSEWORK | | yes | | | +| INTERVAL_TRAINING | | yes | | | +| IN_VEHICLE | | yes | | | +| KAYAKING | | yes | | | +| KETTLEBELL_TRAINING | | yes | | | +| KICK_SCOOTER | | yes | | | +| KITE_SURFING | | yes | | | +| MEDITATION | | yes | | | +| MIXED_MARTIAL_ARTS | | yes | | | +| P90X | | yes | | | +| PARAGLIDING | | yes | yes | | +| POLO | | yes | | | +| ROCK_CLIMBING | (yes) | yes | yes | on iOS this will be stored as CLIMBING | +| RUNNING_JOGGING | (yes) | yes | | on iOS this will be stored as RUNNING | +| RUNNING_SAND | (yes) | yes | | on iOS this will be stored as RUNNING | +| RUNNING_TREADMILL | (yes) | yes | yes | on iOS this will be stored as RUNNING | +| SCUBA_DIVING | | yes | yes | | +| SKATING_CROSS | (yes) | yes | | on iOS this will be stored as SKATING | +| SKATING_INDOOR | (yes) | yes | | on iOS this will be stored as SKATING | +| SKATING_INLINE | (yes) | yes | | on iOS this will be stored as SKATING | +| SKIING_BACK_COUNTRY | | yes | | | +| SKIING_KITE | | yes | | | +| SKIING_ROLLER | | yes | | | +| SLEDDING | | yes | | | +| STAIR_CLIMBING_MACHINE | | yes | yes | | +| STANDUP_PADDLEBOARDING | | yes | | | +| STILL | | yes | | | +| STRENGTH_TRAINING | | yes | yes | | +| SURFING | | yes | yes | | +| SWIMMING_OPEN_WATER | | yes | yes | | +| SWIMMING_POOL | | yes | yes | | +| TEAM_SPORTS | | yes | | | +| TILTING | | yes | | | +| TREADMILL | | yes | | | +| VOLLEYBALL_BEACH | | yes | | | +| VOLLEYBALL_INDOOR | | yes | | | +| WAKEBOARDING | | yes | | | +| WALKING_FITNESS | | yes | | | +| WALKING_NORDIC | | yes | | | +| WALKING_STROLLER | | yes | | | +| WALKING_TREADMILL | | yes | | | +| WEIGHTLIFTING | | yes | yes | | +| WHEELCHAIR | | yes | yes | | +| WINDSURFING | | yes | | | +| ZUMBA | | yes | | | +| OTHER | yes | yes | | | diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 3cbc5e3f9..f8f772608 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -777,15 +777,55 @@ class HealthPlugin(private var channel: MethodChannel? = null) : } private fun writeMealHC(call: MethodCall, result: Result) { - val startTime = Instant.ofEpochMilli(call.argument("startTime")!!) - val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) - val calories = call.argument("caloriesConsumed") - val carbs = call.argument("carbohydrates") as Double? + val startTime = Instant.ofEpochMilli(call.argument("start_time")!!) + val endTime = Instant.ofEpochMilli(call.argument("end_time")!!) + val calories = call.argument("calories") val protein = call.argument("protein") as Double? - val fat = call.argument("fatTotal") as Double? + val carbs = call.argument("carbs") as Double? + val fat = call.argument("fat") as Double? val caffeine = call.argument("caffeine") as Double? + val vitaminA = call.argument("vitamin_a") as Double? + val b1Thiamine = call.argument("b1_thiamine") as Double? + val b2Riboflavin = call.argument("b2_riboflavin") as Double? + val b3Niacin = call.argument("b3_niacin") as Double? + val b5PantothenicAcid = call.argument("b5_pantothenic_acid") as Double? + val b6Pyridoxine = call.argument("b6_pyridoxine") as Double? + val b7Biotin = call.argument("b7_biotin") as Double? + val b9Folate = call.argument("b9_folate") as Double? + val b12Cobalamin = call.argument("b12_cobalamin") as Double? + val vitaminC = call.argument("vitamin_c") as Double? + val vitaminD = call.argument("vitamin_d") as Double? + val vitaminE = call.argument("vitamin_e") as Double? + val vitaminK = call.argument("vitamin_k") as Double? + val calcium = call.argument("calcium") as Double? + val chloride = call.argument("chloride") as Double? + val cholesterol = call.argument("cholesterol") as Double? + // Choline is not yet supported by Health Connect + // val choline = call.argument("choline") as Double? + val chromium = call.argument("chromium") as Double? + val copper = call.argument("copper") as Double? + val fatUnsaturated = call.argument("fat_unsaturated") as Double? + val fatMonounsaturated = call.argument("fat_monounsaturated") as Double? + val fatPolyunsaturated = call.argument("fat_polyunsaturated") as Double? + val fatSaturated = call.argument("fat_saturated") as Double? + val fatTransMonoenoic = call.argument("fat_trans_monoenoic") as Double? + val fiber = call.argument("fiber") as Double? + val iodine = call.argument("iodine") as Double? + val iron = call.argument("iron") as Double? + val magnesium = call.argument("magnesium") as Double? + val manganese = call.argument("manganese") as Double? + val molybdenum = call.argument("molybdenum") as Double? + val phosphorus = call.argument("phosphorus") as Double? + val potassium = call.argument("potassium") as Double? + val selenium = call.argument("selenium") as Double? + val sodium = call.argument("sodium") as Double? + val sugar = call.argument("sugar") as Double? + // Water is not support on a food in Health Connect + // val water = call.argument("water") as Double? + val zinc = call.argument("zinc") as Double? + val name = call.argument("name") - val mealType = call.argument("mealType")!! + val mealType = call.argument("meal_type")!! scope.launch { try { @@ -798,6 +838,41 @@ class HealthPlugin(private var channel: MethodChannel? = null) : protein = protein?.grams, totalFat = fat?.grams, caffeine = caffeine?.grams, + vitaminA = vitaminA?.grams, + thiamin = b1Thiamine?.grams, + riboflavin = b2Riboflavin?.grams, + niacin = b3Niacin?.grams, + pantothenicAcid = b5PantothenicAcid?.grams, + vitaminB6 = b6Pyridoxine?.grams, + biotin = b7Biotin?.grams, + folate = b9Folate?.grams, + vitaminB12 = b12Cobalamin?.grams, + vitaminC = vitaminC?.grams, + vitaminD = vitaminD?.grams, + vitaminE = vitaminE?.grams, + vitaminK = vitaminK?.grams, + calcium = calcium?.grams, + chloride = chloride?.grams, + cholesterol = cholesterol?.grams, + chromium = chromium?.grams, + copper = copper?.grams, + unsaturatedFat = fatUnsaturated?.grams, + monounsaturatedFat = fatMonounsaturated?.grams, + polyunsaturatedFat = fatPolyunsaturated?.grams, + saturatedFat = fatSaturated?.grams, + transFat = fatTransMonoenoic?.grams, + dietaryFiber = fiber?.grams, + iodine = iodine?.grams, + iron = iron?.grams, + magnesium = magnesium?.grams, + manganese = manganese?.grams, + molybdenum = molybdenum?.grams, + phosphorus = phosphorus?.grams, + potassium = potassium?.grams, + selenium = selenium?.grams, + sodium = sodium?.grams, + sugar = sugar?.grams, + zinc = zinc?.grams, startTime = startTime, startZoneOffset = null, endTime = endTime, @@ -840,14 +915,16 @@ class HealthPlugin(private var channel: MethodChannel? = null) : return } - val startTime = call.argument("startTime")!! - val endTime = call.argument("endTime")!! - val calories = call.argument("caloriesConsumed") - val carbs = call.argument("carbohydrates") as Double? + val startTime = call.argument("start_time")!! + val endTime = call.argument("end_time")!! + val calories = call.argument("calories") + val carbs = call.argument("carbs") as Double? val protein = call.argument("protein") as Double? - val fat = call.argument("fatTotal") as Double? + val fat = call.argument("fat") as Double? + + val name = call.argument("name") - val mealType = call.argument("mealType")!! + val mealType = call.argument("meal_type")!! val dataType = DataType.TYPE_NUTRITION @@ -867,7 +944,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : .build() val nutrients = mutableMapOf(Field.NUTRIENT_CALORIES to calories?.toFloat()) - + if (carbs != null) { nutrients[Field.NUTRIENT_TOTAL_CARBS] = carbs.toFloat() } @@ -887,7 +964,11 @@ class HealthPlugin(private var channel: MethodChannel? = null) : endTime, TimeUnit.MILLISECONDS ) - .setField(Field.FIELD_NUTRIENTS, nutrients) + .setField( + Field.FIELD_NUTRIENTS, + // Remove null values + nutrients.filterValues { it != null }.toMutableMap(), + ) if (name != null) { dataBuilder.setField(Field.FIELD_FOOD_ITEM, name as String) @@ -2961,7 +3042,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : } // TODO: Find alternative to SOURCE_ID or make it nullable? - fun convertRecord(record: Any, dataType: String): List> { + fun convertRecord(record: Any, dataType: String): List> { val metadata = (record as Record).metadata when (record) { is WeightRecord -> @@ -3323,19 +3404,51 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ) is NutritionRecord -> return listOf( - mapOf( - "calories" to - record.energy!!.inKilocalories, - "protein" to - record.protein!!.inGrams, - "carbs" to - record.totalCarbohydrate!! - .inGrams, - "fat" to - record.totalFat!! - .inGrams, + mapOf( + "calories" to record.energy?.inKilocalories, + "protein" to record.protein?.inGrams, + "carbs" to record.totalCarbohydrate?.inGrams, + "fat" to record.totalFat?.inGrams, + "caffeine" to record.caffeine?.inGrams, + "vitamin_a" to record.vitaminA?.inGrams, + "b1_thiamine" to record.thiamin?.inGrams, + "b2_riboflavin" to record.riboflavin?.inGrams, + "b3_niacin" to record.niacin?.inGrams, + "b5_pantothenic_acid" to record.pantothenicAcid?.inGrams, + "b6_pyridoxine" to record.vitaminB6?.inGrams, + "b7_biotin" to record.biotin?.inGrams, + "b9_folate" to record.folate?.inGrams, + "b12_cobalamin" to record.vitaminB12?.inGrams, + "vitamin_c" to record.vitaminC?.inGrams, + "vitamin_d" to record.vitaminD?.inGrams, + "vitamin_e" to record.vitaminE?.inGrams, + "vitamin_k" to record.vitaminK?.inGrams, + "calcium" to record.calcium?.inGrams, + "chloride" to record.chloride?.inGrams, + "cholesterol" to record.cholesterol?.inGrams, + "choline" to null, + "chromium" to record.chromium?.inGrams, + "copper" to record.copper?.inGrams, + "fat_unsaturated" to record.unsaturatedFat?.inGrams, + "fat_monounsaturated" to record.monounsaturatedFat?.inGrams, + "fat_polyunsaturated" to record.polyunsaturatedFat?.inGrams, + "fat_saturated" to record.saturatedFat?.inGrams, + "fat_trans_monoenoic" to record.transFat?.inGrams, + "fiber" to record.dietaryFiber?.inGrams, + "iodine" to record.iodine?.inGrams, + "iron" to record.iron?.inGrams, + "magnesium" to record.magnesium?.inGrams, + "manganese" to record.manganese?.inGrams, + "molybdenum" to record.molybdenum?.inGrams, + "phosphorus" to record.phosphorus?.inGrams, + "potassium" to record.potassium?.inGrams, + "selenium" to record.selenium?.inGrams, + "sodium" to record.sodium?.inGrams, + "sugar" to record.sugar?.inGrams, + "water" to null, + "zinc" to record.zinc?.inGrams, "name" to record.name!!, - "mealType" to + "meal_type" to (MapTypeToMealTypeHC[ record.mealType] ?: MEAL_TYPE_UNKNOWN), diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index 412099989..4d06749da 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -272,6 +272,42 @@ class _HealthAppState extends State { fatTotal: 50, name: "Banana", caffeine: 0.002, + vitaminA: 0.001, + vitaminC: 0.002, + vitaminD: 0.003, + vitaminE: 0.004, + vitaminK: 0.005, + b1Thiamin: 0.006, + b2Riboflavin: 0.007, + b3Niacin: 0.008, + b5PantothenicAcid: 0.009, + b6Pyridoxine: 0.010, + b7Biotin: 0.011, + b9Folate: 0.012, + b12Cobalamin: 0.013, + calcium: 0.015, + copper: 0.016, + iodine: 0.017, + iron: 0.018, + magnesium: 0.019, + manganese: 0.020, + phosphorus: 0.021, + potassium: 0.022, + selenium: 0.023, + sodium: 0.024, + zinc: 0.025, + water: 0.026, + molybdenum: 0.027, + chloride: 0.028, + chromium: 0.029, + cholesterol: 0.030, + fiber: 0.031, + fatMonounsaturated: 0.032, + fatPolyunsaturated: 0.033, + fatUnsaturated: 0.065, + fatTransMonoenoic: 0.65, + fatSaturated: 066, + sugar: 0.067, ); // Store an Audiogram - only available on iOS diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 9f8ba3ba2..5c6780539 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -14,6 +14,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { var dataQuantityTypesDict: [String: HKQuantityType] = [:] var unitDict: [String: HKUnit] = [:] var workoutActivityTypeMap: [String: HKWorkoutActivityType] = [:] + var nutritionList: [String] = [] // Health Data Type Keys let ACTIVE_ENERGY_BURNED = "ACTIVE_ENERGY_BURNED" @@ -26,11 +27,89 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let BODY_FAT_PERCENTAGE = "BODY_FAT_PERCENTAGE" let BODY_MASS_INDEX = "BODY_MASS_INDEX" let BODY_TEMPERATURE = "BODY_TEMPERATURE" + // Nutrition let DIETARY_CARBS_CONSUMED = "DIETARY_CARBS_CONSUMED" let DIETARY_ENERGY_CONSUMED = "DIETARY_ENERGY_CONSUMED" let DIETARY_FATS_CONSUMED = "DIETARY_FATS_CONSUMED" let DIETARY_PROTEIN_CONSUMED = "DIETARY_PROTEIN_CONSUMED" let DIETARY_CAFFEINE = "DIETARY_CAFFEINE" + let DIETARY_FIBER = "DIETARY_FIBER" + let DIETARY_SUGAR = "DIETARY_SUGAR" + let DIETARY_FAT_MONOUNSATURATED = "DIETARY_FAT_MONOUNSATURATED" + let DIETARY_FAT_POLYUNSATURATED = "DIETARY_FAT_POLYUNSATURATED" + let DIETARY_FAT_SATURATED = "DIETARY_FAT_SATURATED" + let DIETARY_CHOLESTEROL = "DIETARY_CHOLESTEROL" + let DIETARY_VITAMIN_A = "DIETARY_VITAMIN_A" + let DIETARY_THIAMIN = "DIETARY_THIAMIN" + let DIETARY_RIBOFLAVIN = "DIETARY_RIBOFLAVIN" + let DIETARY_NIACIN = "DIETARY_NIACIN" + let DIETARY_PANTOTHENIC_ACID = "DIETARY_PANTOTHENIC_ACID" + let DIETARY_VITAMIN_B6 = "DIETARY_VITAMIN_B6" + let DIETARY_BIOTIN = "DIETARY_BIOTIN" + let DIETARY_VITAMIN_B12 = "DIETARY_VITAMIN_B12" + let DIETARY_VITAMIN_C = "DIETARY_VITAMIN_C" + let DIETARY_VITAMIN_D = "DIETARY_VITAMIN_D" + let DIETARY_VITAMIN_E = "DIETARY_VITAMIN_E" + let DIETARY_VITAMIN_K = "DIETARY_VITAMIN_K" + let DIETARY_FOLATE = "DIETARY_FOLATE" + let DIETARY_CALCIUM = "DIETARY_CALCIUM" + let DIETARY_CHLORIDE = "DIETARY_CHLORIDE" + let DIETARY_IRON = "DIETARY_IRON" + let DIETARY_MAGNESIUM = "DIETARY_MAGNESIUM" + let DIETARY_PHOSPHORUS = "DIETARY_PHOSPHORUS" + let DIETARY_POTASSIUM = "DIETARY_POTASSIUM" + let DIETARY_SODIUM = "DIETARY_SODIUM" + let DIETARY_ZINC = "DIETARY_ZINC" + let DIETARY_WATER = "WATER" + let DIETARY_CHROMIUM = "DIETARY_CHROMIUM" + let DIETARY_COPPER = "DIETARY_COPPER" + let DIETARY_IODINE = "DIETARY_IODINE" + let DIETARY_MANGANESE = "DIETARY_MANGANESE" + let DIETARY_MOLYBDENUM = "DIETARY_MOLYBDENUM" + let DIETARY_SELENIUM = "DIETARY_SELENIUM" + let NUTRITION_KEYS: [String: HKQuantityTypeIdentifier] = [ + "calories": .dietaryEnergyConsumed, + "protein": .dietaryProtein, + "carbs": .dietaryCarbohydrates, + "fat": .dietaryFatTotal, + "caffeine": .dietaryCaffeine, + "vitamin_a": .dietaryVitaminA, + "b1_thiamine": .dietaryThiamin, + "b2_riboflavin": .dietaryRiboflavin, + "b3_niacin" : .dietaryNiacin, + "b5_pantothenic_acid" : .dietaryPantothenicAcid, + "b6_pyridoxine" : .dietaryVitaminB6, + "b7_biotin" : .dietaryBiotin, + "b9_folate" : .dietaryFolate, + "b12_cobalamin": .dietaryVitaminB12, + "vitamin_c": .dietaryVitaminC, + "vitamin_d": .dietaryVitaminD, + "vitamin_e": .dietaryVitaminE, + "vitamin_k": .dietaryVitaminK, + "calcium": .dietaryCalcium, + "chloride": .dietaryChloride, + "cholesterol": .dietaryCholesterol, + "chromium": .dietaryChromium, + "copper": .dietaryCopper, + "fat_unsaturated": .dietaryFatMonounsaturated, + "fat_monounsaturated": .dietaryFatMonounsaturated, + "fat_polyunsaturated": .dietaryFatPolyunsaturated, + "fat_saturated": .dietaryFatSaturated, + // "fat_trans_monoenoic": .dietaryFatTransMonoenoic, + "fiber": .dietaryFiber, + "iodine": .dietaryIodine, + "iron": .dietaryIron, + "magnesium": .dietaryMagnesium, + "manganese": .dietaryManganese, + "molybdenum": .dietaryMolybdenum, + "phosphorus": .dietaryPhosphorus, + "potassium": .dietaryPotassium, + "selenium": .dietarySelenium, + "sodium": .dietarySodium, + "sugar": .dietarySugar, + "water": .dietaryWater, + "zinc": .dietaryZinc, + ] let ELECTRODERMAL_ACTIVITY = "ELECTRODERMAL_ACTIVITY" let FORCED_EXPIRATORY_VOLUME = "FORCED_EXPIRATORY_VOLUME" let HEART_RATE = "HEART_RATE" @@ -51,7 +130,6 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let DISTANCE_SWIMMING = "DISTANCE_SWIMMING" let DISTANCE_CYCLING = "DISTANCE_CYCLING" let FLIGHTS_CLIMBED = "FLIGHTS_CLIMBED" - let WATER = "WATER" let MINDFULNESS = "MINDFULNESS" let SLEEP_IN_BED = "SLEEP_IN_BED" let SLEEP_ASLEEP = "SLEEP_ASLEEP" @@ -227,17 +305,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { types.remove(at: nutritionIndex) let nutritionPermission = permissions[nutritionIndex] permissions.remove(at: nutritionIndex) - - types.append(DIETARY_ENERGY_CONSUMED) - permissions.append(nutritionPermission) - types.append(DIETARY_CARBS_CONSUMED) - permissions.append(nutritionPermission) - types.append(DIETARY_PROTEIN_CONSUMED) - permissions.append(nutritionPermission) - types.append(DIETARY_FATS_CONSUMED) - permissions.append(nutritionPermission) - types.append(DIETARY_CAFFEINE) - permissions.append(nutritionPermission) + + for nutritionType in nutritionList { + types.append(nutritionType) + permissions.append(nutritionPermission) + } } for (index, type) in types.enumerated() { @@ -282,16 +354,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { var typesToWrite = Set() for (index, key) in types.enumerated() { if (key == NUTRITION) { - let caloriesType = dataTypeLookUp(key: DIETARY_ENERGY_CONSUMED) - let carbsType = dataTypeLookUp(key: DIETARY_CARBS_CONSUMED) - let proteinType = dataTypeLookUp(key: DIETARY_PROTEIN_CONSUMED) - let fatType = dataTypeLookUp(key: DIETARY_FATS_CONSUMED) - let caffeineType = dataTypeLookUp(key: DIETARY_CAFFEINE) - typesToWrite.insert(caloriesType); - typesToWrite.insert(carbsType); - typesToWrite.insert(proteinType); - typesToWrite.insert(fatType); - typesToWrite.insert(caffeineType); + for nutritionType in nutritionList { + let nutritionData = dataTypeLookUp(key: nutritionType) + typesToWrite.insert(nutritionData) + } } else { let dataType = dataTypeLookUp(key: key) let access = permissions[index] @@ -450,15 +516,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { func writeMeal(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, - let startTime = (arguments["startTime"] as? NSNumber), - let endTime = (arguments["endTime"] as? NSNumber), - let calories = (arguments["caloriesConsumed"] as? Double?) ?? 0, - let carbs = (arguments["carbohydrates"] as? Double?) ?? 0, - let protein = (arguments["protein"] as? Double?) ?? 0, - let fat = (arguments["fatTotal"] as? Double?) ?? 0, - let name = (arguments["name"] as? String?), - let caffeine = (arguments["caffeine"] as? Double?) ?? 0, - let mealType = (arguments["mealType"] as? String?) + let name = (arguments["name"] as? String?), + let startTime = (arguments["start_time"] as? NSNumber), + let endTime = (arguments["end_time"] as? NSNumber), + let mealType = (arguments["meal_type"] as? String?) else { throw PluginError(message: "Invalid Arguments") } @@ -466,7 +527,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - var mealTypeString = mealType ?? "UNKNOWN" + let mealTypeString = mealType ?? "UNKNOWN" var metadata = ["HKFoodMeal": "\(mealTypeString)"] if(name != nil) { @@ -474,22 +535,15 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } var nutrition = Set() + for (key, identifier) in NUTRITION_KEYS { + let value = arguments[key] as? Double + guard let unwrappedValue = value else { continue } + let unit = key == "calories" ? HKUnit.kilocalorie() : key == "water" ? HKUnit.literUnit(with: .milli) : HKUnit.gram() + let nutritionSample = HKQuantitySample( + type: HKSampleType.quantityType(forIdentifier: identifier)!, quantity: HKQuantity(unit: unit, doubleValue: unwrappedValue), start: dateFrom, end: dateTo, metadata: metadata) + nutrition.insert(nutritionSample) + } - let caloriesSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryEnergyConsumed)!, quantity: HKQuantity(unit: HKUnit.kilocalorie(), doubleValue: calories), start: dateFrom, end: dateTo, metadata: metadata) - nutrition.insert(caloriesSample) - - let carbsSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryCarbohydrates)!, quantity: HKQuantity(unit: HKUnit.gram(), doubleValue: carbs), start: dateFrom, end: dateTo, metadata: metadata) - nutrition.insert(carbsSample) - - let proteinSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryProtein)!, quantity: HKQuantity(unit: HKUnit.gram(), doubleValue: protein), start: dateFrom, end: dateTo, metadata: metadata) - nutrition.insert(proteinSample) - - let fatSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryFatTotal)!, quantity: HKQuantity(unit: HKUnit.gram(), doubleValue: fat), start: dateFrom, end: dateTo, metadata: metadata) - nutrition.insert(fatSample) - - let caffeineSample = HKQuantitySample(type: HKSampleType.quantityType(forIdentifier: .dietaryCaffeine)!, quantity: HKQuantity(unit: HKUnit.gram(), doubleValue: caffeine), start: dateFrom, end: dateTo, metadata: metadata) - nutrition.insert(caffeineSample) - if #available(iOS 15.0, *){ let type = HKCorrelationType.correlationType(forIdentifier: HKCorrelationTypeIdentifier.food)! let meal = HKCorrelation(type: type, start: dateFrom, end: dateTo, objects: nutrition, metadata: metadata) @@ -781,51 +835,38 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } case let (nutritionSample as [HKCorrelation]) as Any: - - //let samples = nutritionSample[0].objects(for: HKObjectType.quantityType(forIdentifier: .dietaryEnergyConsumed)!) - var calories = 0.0 - var fat = 0.0 - var carbs = 0.0 - var protein = 0.0 - - let name = nutritionSample[0].metadata?[HKMetadataKeyFoodType] as! String - let mealType = nutritionSample[0].metadata?["HKFoodMeal"] - let samples = nutritionSample[0].objects - for sample in samples { - if let quantitySample = sample as? HKQuantitySample { - if (quantitySample.quantityType == HKObjectType.quantityType(forIdentifier: .dietaryEnergyConsumed)){ - calories = quantitySample.quantity.doubleValue(for: HKUnit.kilocalorie()) - } - if (quantitySample.quantityType == HKObjectType.quantityType(forIdentifier: .dietaryCarbohydrates)){ - carbs = quantitySample.quantity.doubleValue(for: HKUnit.gram()) - } - if (quantitySample.quantityType == HKObjectType.quantityType(forIdentifier: .dietaryProtein)){ - protein = quantitySample.quantity.doubleValue(for: HKUnit.gram()) - } - if (quantitySample.quantityType == HKObjectType.quantityType(forIdentifier: .dietaryFatTotal)){ - fat = quantitySample.quantity.doubleValue(for: HKUnit.gram()) + var foods: [[String: Any?]] = [] + for food in nutritionSample { + let name = food.metadata?[HKMetadataKeyFoodType] as? String + let mealType = food.metadata?["HKFoodMeal"] + let samples = food.objects + // get first sample if it exists + if let sample = samples.first as? HKQuantitySample { + var sampleDict = [ + "uuid": "\(sample.uuid)", + "name": name, + "meal_type": mealType, + "date_from": Int(sample.startDate.timeIntervalSince1970 * 1000), + "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), + "source_id": sample.sourceRevision.source.bundleIdentifier, + "source_name": sample.sourceRevision.source.name, + ] + for sample in samples { + if let quantitySample = sample as? HKQuantitySample { + for (key, identifier) in NUTRITION_KEYS { + if (quantitySample.quantityType == HKObjectType.quantityType(forIdentifier: identifier)){ + let unit = key == "calories" ? HKUnit.kilocalorie() : key == "water" ? HKUnit.literUnit(with: .milli) : HKUnit.gram() + sampleDict[key] = quantitySample.quantity.doubleValue(for: unit) + } + } + } } + foods.append(sampleDict) } } - - let dictionaries = nutritionSample.map { sample -> NSDictionary in - return [ - "uuid": "\(sample.uuid)", - "calories": calories, - "carbs": carbs, - "protein": protein, - "fat": fat, - "name": name, - "mealType": mealType, - "date_from": Int(sample.startDate.timeIntervalSince1970 * 1000), - "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), - "source_id": sample.sourceRevision.source.bundleIdentifier, - "source_name": sample.sourceRevision.source.name, - ] - } DispatchQueue.main.async { - result(dictionaries) + result(foods) } default: @@ -1162,7 +1203,18 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { workoutActivityTypeMap["TAI_CHI"] = .taiChi workoutActivityTypeMap["WRESTLING"] = .wrestling workoutActivityTypeMap["OTHER"] = .other - + nutritionList = [ + DIETARY_ENERGY_CONSUMED, DIETARY_CARBS_CONSUMED, DIETARY_PROTEIN_CONSUMED, + DIETARY_FATS_CONSUMED, DIETARY_CAFFEINE, DIETARY_FIBER, DIETARY_SUGAR, + DIETARY_FAT_MONOUNSATURATED, DIETARY_FAT_POLYUNSATURATED, DIETARY_FAT_SATURATED, + DIETARY_CHOLESTEROL, DIETARY_VITAMIN_A, DIETARY_THIAMIN, DIETARY_RIBOFLAVIN, + DIETARY_NIACIN, DIETARY_PANTOTHENIC_ACID, DIETARY_VITAMIN_B6, DIETARY_BIOTIN, + DIETARY_VITAMIN_B12, DIETARY_VITAMIN_C, DIETARY_VITAMIN_D, DIETARY_VITAMIN_E, + DIETARY_VITAMIN_K, DIETARY_FOLATE, DIETARY_CALCIUM, DIETARY_CHLORIDE, + DIETARY_IRON, DIETARY_MAGNESIUM, DIETARY_PHOSPHORUS, DIETARY_POTASSIUM, + DIETARY_SODIUM, DIETARY_ZINC, DIETARY_WATER, DIETARY_CHROMIUM, DIETARY_COPPER, + DIETARY_IODINE, DIETARY_MANGANESE, DIETARY_MOLYBDENUM, DIETARY_SELENIUM, + ] // Set up iOS 13 specific types (ordinary health data types) if #available(iOS 13.0, *) { dataTypesDict[ACTIVE_ENERGY_BURNED] = HKSampleType.quantityType( @@ -1184,16 +1236,48 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { forIdentifier: .bodyFatPercentage)! dataTypesDict[BODY_MASS_INDEX] = HKSampleType.quantityType(forIdentifier: .bodyMassIndex)! dataTypesDict[BODY_TEMPERATURE] = HKSampleType.quantityType(forIdentifier: .bodyTemperature)! - dataTypesDict[DIETARY_CARBS_CONSUMED] = HKSampleType.quantityType( - forIdentifier: .dietaryCarbohydrates)! - dataTypesDict[DIETARY_ENERGY_CONSUMED] = HKSampleType.quantityType( - forIdentifier: .dietaryEnergyConsumed)! - dataTypesDict[DIETARY_CAFFEINE] = HKSampleType.quantityType( - forIdentifier: .dietaryCaffeine)! - dataTypesDict[DIETARY_FATS_CONSUMED] = HKSampleType.quantityType( - forIdentifier: .dietaryFatTotal)! - dataTypesDict[DIETARY_PROTEIN_CONSUMED] = HKSampleType.quantityType( - forIdentifier: .dietaryProtein)! + + // Nutrition + dataTypesDict[DIETARY_CARBS_CONSUMED] = HKSampleType.quantityType(forIdentifier: .dietaryCarbohydrates)! + dataTypesDict[DIETARY_CAFFEINE] = HKSampleType.quantityType(forIdentifier: .dietaryCaffeine)! + dataTypesDict[DIETARY_ENERGY_CONSUMED] = HKSampleType.quantityType(forIdentifier: .dietaryEnergyConsumed)! + dataTypesDict[DIETARY_FATS_CONSUMED] = HKSampleType.quantityType(forIdentifier: .dietaryFatTotal)! + dataTypesDict[DIETARY_PROTEIN_CONSUMED] = HKSampleType.quantityType(forIdentifier: .dietaryProtein)! + dataTypesDict[DIETARY_FIBER] = HKSampleType.quantityType(forIdentifier: .dietaryFiber)! + dataTypesDict[DIETARY_SUGAR] = HKSampleType.quantityType(forIdentifier: .dietarySugar)! + dataTypesDict[DIETARY_FAT_MONOUNSATURATED] = HKSampleType.quantityType(forIdentifier: .dietaryFatMonounsaturated)! + dataTypesDict[DIETARY_FAT_POLYUNSATURATED] = HKSampleType.quantityType(forIdentifier: .dietaryFatPolyunsaturated)! + dataTypesDict[DIETARY_FAT_SATURATED] = HKSampleType.quantityType(forIdentifier: .dietaryFatSaturated)! + dataTypesDict[DIETARY_CHOLESTEROL] = HKSampleType.quantityType(forIdentifier: .dietaryCholesterol)! + dataTypesDict[DIETARY_VITAMIN_A] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminA)! + dataTypesDict[DIETARY_THIAMIN] = HKSampleType.quantityType(forIdentifier: .dietaryThiamin)! + dataTypesDict[DIETARY_RIBOFLAVIN] = HKSampleType.quantityType(forIdentifier: .dietaryRiboflavin)! + dataTypesDict[DIETARY_NIACIN] = HKSampleType.quantityType(forIdentifier: .dietaryNiacin)! + dataTypesDict[DIETARY_PANTOTHENIC_ACID] = HKSampleType.quantityType(forIdentifier: .dietaryPantothenicAcid)! + dataTypesDict[DIETARY_VITAMIN_B6] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminB6)! + dataTypesDict[DIETARY_BIOTIN] = HKSampleType.quantityType(forIdentifier: .dietaryBiotin)! + dataTypesDict[DIETARY_VITAMIN_B12] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminB12)! + dataTypesDict[DIETARY_VITAMIN_C] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminC)! + dataTypesDict[DIETARY_VITAMIN_D] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminD)! + dataTypesDict[DIETARY_VITAMIN_E] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminE)! + dataTypesDict[DIETARY_VITAMIN_K] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminK)! + dataTypesDict[DIETARY_FOLATE] = HKSampleType.quantityType(forIdentifier: .dietaryFolate)! + dataTypesDict[DIETARY_CALCIUM] = HKSampleType.quantityType(forIdentifier: .dietaryCalcium)! + dataTypesDict[DIETARY_CHLORIDE] = HKSampleType.quantityType(forIdentifier: .dietaryChloride)! + dataTypesDict[DIETARY_IRON] = HKSampleType.quantityType(forIdentifier: .dietaryIron)! + dataTypesDict[DIETARY_MAGNESIUM] = HKSampleType.quantityType(forIdentifier: .dietaryMagnesium)! + dataTypesDict[DIETARY_PHOSPHORUS] = HKSampleType.quantityType(forIdentifier: .dietaryPhosphorus)! + dataTypesDict[DIETARY_POTASSIUM] = HKSampleType.quantityType(forIdentifier: .dietaryPotassium)! + dataTypesDict[DIETARY_SODIUM] = HKSampleType.quantityType(forIdentifier: .dietarySodium)! + dataTypesDict[DIETARY_ZINC] = HKSampleType.quantityType(forIdentifier: .dietaryZinc)! + dataTypesDict[DIETARY_WATER] = HKSampleType.quantityType(forIdentifier: .dietaryWater)! + dataTypesDict[DIETARY_CHROMIUM] = HKSampleType.quantityType(forIdentifier: .dietaryChromium)! + dataTypesDict[DIETARY_COPPER] = HKSampleType.quantityType(forIdentifier: .dietaryCopper)! + dataTypesDict[DIETARY_IODINE] = HKSampleType.quantityType(forIdentifier: .dietaryIodine)! + dataTypesDict[DIETARY_MANGANESE] = HKSampleType.quantityType(forIdentifier: .dietaryManganese)! + dataTypesDict[DIETARY_MOLYBDENUM] = HKSampleType.quantityType(forIdentifier: .dietaryMolybdenum)! + dataTypesDict[DIETARY_SELENIUM] = HKSampleType.quantityType(forIdentifier: .dietarySelenium)! + dataTypesDict[ELECTRODERMAL_ACTIVITY] = HKSampleType.quantityType( forIdentifier: .electrodermalActivity)! dataTypesDict[FORCED_EXPIRATORY_VOLUME] = HKSampleType.quantityType( @@ -1216,7 +1300,6 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataTypesDict[DISTANCE_SWIMMING] = HKSampleType.quantityType(forIdentifier: .distanceSwimming)! dataTypesDict[DISTANCE_CYCLING] = HKSampleType.quantityType(forIdentifier: .distanceCycling)! dataTypesDict[FLIGHTS_CLIMBED] = HKSampleType.quantityType(forIdentifier: .flightsClimbed)! - dataTypesDict[WATER] = HKSampleType.quantityType(forIdentifier: .dietaryWater)! dataTypesDict[MINDFULNESS] = HKSampleType.categoryType(forIdentifier: .mindfulSession)! dataTypesDict[SLEEP_IN_BED] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! dataTypesDict[SLEEP_ASLEEP] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! @@ -1246,10 +1329,48 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataQuantityTypesDict[BODY_FAT_PERCENTAGE] = HKQuantityType.quantityType(forIdentifier: .bodyFatPercentage)! dataQuantityTypesDict[BODY_MASS_INDEX] = HKQuantityType.quantityType(forIdentifier: .bodyMassIndex)! dataQuantityTypesDict[BODY_TEMPERATURE] = HKQuantityType.quantityType(forIdentifier: .bodyTemperature)! - dataQuantityTypesDict[DIETARY_CARBS_CONSUMED] = HKQuantityType.quantityType(forIdentifier: .dietaryCarbohydrates)! - dataQuantityTypesDict[DIETARY_ENERGY_CONSUMED] = HKQuantityType.quantityType(forIdentifier: .dietaryEnergyConsumed)! - dataQuantityTypesDict[DIETARY_FATS_CONSUMED] = HKQuantityType.quantityType(forIdentifier: .dietaryFatTotal)! - dataQuantityTypesDict[DIETARY_PROTEIN_CONSUMED] = HKQuantityType.quantityType(forIdentifier: .dietaryProtein)! + + // Nutrition + dataQuantityTypesDict[DIETARY_CARBS_CONSUMED] = HKSampleType.quantityType(forIdentifier: .dietaryCarbohydrates)! + dataQuantityTypesDict[DIETARY_CAFFEINE] = HKSampleType.quantityType(forIdentifier: .dietaryCaffeine)! + dataQuantityTypesDict[DIETARY_ENERGY_CONSUMED] = HKSampleType.quantityType(forIdentifier: .dietaryEnergyConsumed)! + dataQuantityTypesDict[DIETARY_FATS_CONSUMED] = HKSampleType.quantityType(forIdentifier: .dietaryFatTotal)! + dataQuantityTypesDict[DIETARY_PROTEIN_CONSUMED] = HKSampleType.quantityType(forIdentifier: .dietaryProtein)! + dataQuantityTypesDict[DIETARY_FIBER] = HKSampleType.quantityType(forIdentifier: .dietaryFiber)! + dataQuantityTypesDict[DIETARY_SUGAR] = HKSampleType.quantityType(forIdentifier: .dietarySugar)! + dataQuantityTypesDict[DIETARY_FAT_MONOUNSATURATED] = HKSampleType.quantityType(forIdentifier: .dietaryFatMonounsaturated)! + dataQuantityTypesDict[DIETARY_FAT_POLYUNSATURATED] = HKSampleType.quantityType(forIdentifier: .dietaryFatPolyunsaturated)! + dataQuantityTypesDict[DIETARY_FAT_SATURATED] = HKSampleType.quantityType(forIdentifier: .dietaryFatSaturated)! + dataQuantityTypesDict[DIETARY_CHOLESTEROL] = HKSampleType.quantityType(forIdentifier: .dietaryCholesterol)! + dataQuantityTypesDict[DIETARY_VITAMIN_A] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminA)! + dataQuantityTypesDict[DIETARY_THIAMIN] = HKSampleType.quantityType(forIdentifier: .dietaryThiamin)! + dataQuantityTypesDict[DIETARY_RIBOFLAVIN] = HKSampleType.quantityType(forIdentifier: .dietaryRiboflavin)! + dataQuantityTypesDict[DIETARY_NIACIN] = HKSampleType.quantityType(forIdentifier: .dietaryNiacin)! + dataQuantityTypesDict[DIETARY_PANTOTHENIC_ACID] = HKSampleType.quantityType(forIdentifier: .dietaryPantothenicAcid)! + dataQuantityTypesDict[DIETARY_VITAMIN_B6] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminB6)! + dataQuantityTypesDict[DIETARY_BIOTIN] = HKSampleType.quantityType(forIdentifier: .dietaryBiotin)! + dataQuantityTypesDict[DIETARY_VITAMIN_B12] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminB12)! + dataQuantityTypesDict[DIETARY_VITAMIN_C] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminC)! + dataQuantityTypesDict[DIETARY_VITAMIN_D] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminD)! + dataQuantityTypesDict[DIETARY_VITAMIN_E] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminE)! + dataQuantityTypesDict[DIETARY_VITAMIN_K] = HKSampleType.quantityType(forIdentifier: .dietaryVitaminK)! + dataQuantityTypesDict[DIETARY_FOLATE] = HKSampleType.quantityType(forIdentifier: .dietaryFolate)! + dataQuantityTypesDict[DIETARY_CALCIUM] = HKSampleType.quantityType(forIdentifier: .dietaryCalcium)! + dataQuantityTypesDict[DIETARY_CHLORIDE] = HKSampleType.quantityType(forIdentifier: .dietaryChloride)! + dataQuantityTypesDict[DIETARY_IRON] = HKSampleType.quantityType(forIdentifier: .dietaryIron)! + dataQuantityTypesDict[DIETARY_MAGNESIUM] = HKSampleType.quantityType(forIdentifier: .dietaryMagnesium)! + dataQuantityTypesDict[DIETARY_PHOSPHORUS] = HKSampleType.quantityType(forIdentifier: .dietaryPhosphorus)! + dataQuantityTypesDict[DIETARY_POTASSIUM] = HKSampleType.quantityType(forIdentifier: .dietaryPotassium)! + dataQuantityTypesDict[DIETARY_SODIUM] = HKSampleType.quantityType(forIdentifier: .dietarySodium)! + dataQuantityTypesDict[DIETARY_ZINC] = HKSampleType.quantityType(forIdentifier: .dietaryZinc)! + dataQuantityTypesDict[DIETARY_WATER] = HKSampleType.quantityType(forIdentifier: .dietaryWater)! + dataQuantityTypesDict[DIETARY_CHROMIUM] = HKSampleType.quantityType(forIdentifier: .dietaryChromium)! + dataQuantityTypesDict[DIETARY_COPPER] = HKSampleType.quantityType(forIdentifier: .dietaryCopper)! + dataQuantityTypesDict[DIETARY_IODINE] = HKSampleType.quantityType(forIdentifier: .dietaryIodine)! + dataQuantityTypesDict[DIETARY_MANGANESE] = HKSampleType.quantityType(forIdentifier: .dietaryManganese)! + dataQuantityTypesDict[DIETARY_MOLYBDENUM] = HKSampleType.quantityType(forIdentifier: .dietaryMolybdenum)! + dataQuantityTypesDict[DIETARY_SELENIUM] = HKSampleType.quantityType(forIdentifier: .dietarySelenium)! + dataQuantityTypesDict[ELECTRODERMAL_ACTIVITY] = HKQuantityType.quantityType(forIdentifier: .electrodermalActivity)! dataQuantityTypesDict[FORCED_EXPIRATORY_VOLUME] = HKQuantityType.quantityType(forIdentifier: .forcedExpiratoryVolume1)! dataQuantityTypesDict[HEART_RATE] = HKQuantityType.quantityType(forIdentifier: .heartRate)! @@ -1264,7 +1385,6 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataQuantityTypesDict[DISTANCE_SWIMMING] = HKQuantityType.quantityType(forIdentifier: .distanceSwimming)! dataQuantityTypesDict[DISTANCE_CYCLING] = HKQuantityType.quantityType(forIdentifier: .distanceCycling)! dataQuantityTypesDict[FLIGHTS_CLIMBED] = HKQuantityType.quantityType(forIdentifier: .flightsClimbed)! - dataQuantityTypesDict[WATER] = HKQuantityType.quantityType(forIdentifier: .dietaryWater)! healthDataQuantityTypes = Array(dataQuantityTypesDict.values) } diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart index 43941092d..bd80fffcb 100644 --- a/packages/health/lib/health.g.dart +++ b/packages/health/lib/health.g.dart @@ -68,6 +68,39 @@ const _$HealthDataTypeEnumMap = { HealthDataType.DIETARY_ENERGY_CONSUMED: 'DIETARY_ENERGY_CONSUMED', HealthDataType.DIETARY_FATS_CONSUMED: 'DIETARY_FATS_CONSUMED', HealthDataType.DIETARY_PROTEIN_CONSUMED: 'DIETARY_PROTEIN_CONSUMED', + HealthDataType.DIETARY_FIBER: 'DIETARY_FIBER', + HealthDataType.DIETARY_SUGAR: 'DIETARY_SUGAR', + HealthDataType.DIETARY_FAT_MONOUNSATURATED: 'DIETARY_FAT_MONOUNSATURATED', + HealthDataType.DIETARY_FAT_POLYUNSATURATED: 'DIETARY_FAT_POLYUNSATURATED', + HealthDataType.DIETARY_FAT_SATURATED: 'DIETARY_FAT_SATURATED', + HealthDataType.DIETARY_CHOLESTEROL: 'DIETARY_CHOLESTEROL', + HealthDataType.DIETARY_VITAMIN_A: 'DIETARY_VITAMIN_A', + HealthDataType.DIETARY_THIAMIN: 'DIETARY_THIAMIN', + HealthDataType.DIETARY_RIBOFLAVIN: 'DIETARY_RIBOFLAVIN', + HealthDataType.DIETARY_NIACIN: 'DIETARY_NIACIN', + HealthDataType.DIETARY_PANTOTHENIC_ACID: 'DIETARY_PANTOTHENIC_ACID', + HealthDataType.DIETARY_VITAMIN_B6: 'DIETARY_VITAMIN_B6', + HealthDataType.DIETARY_BIOTIN: 'DIETARY_BIOTIN', + HealthDataType.DIETARY_VITAMIN_B12: 'DIETARY_VITAMIN_B12', + HealthDataType.DIETARY_VITAMIN_C: 'DIETARY_VITAMIN_C', + HealthDataType.DIETARY_VITAMIN_D: 'DIETARY_VITAMIN_D', + HealthDataType.DIETARY_VITAMIN_E: 'DIETARY_VITAMIN_E', + HealthDataType.DIETARY_VITAMIN_K: 'DIETARY_VITAMIN_K', + HealthDataType.DIETARY_FOLATE: 'DIETARY_FOLATE', + HealthDataType.DIETARY_CALCIUM: 'DIETARY_CALCIUM', + HealthDataType.DIETARY_CHLORIDE: 'DIETARY_CHLORIDE', + HealthDataType.DIETARY_IRON: 'DIETARY_IRON', + HealthDataType.DIETARY_MAGNESIUM: 'DIETARY_MAGNESIUM', + HealthDataType.DIETARY_PHOSPHORUS: 'DIETARY_PHOSPHORUS', + HealthDataType.DIETARY_POTASSIUM: 'DIETARY_POTASSIUM', + HealthDataType.DIETARY_SODIUM: 'DIETARY_SODIUM', + HealthDataType.DIETARY_ZINC: 'DIETARY_ZINC', + HealthDataType.DIETARY_CHROMIUM: 'DIETARY_CHROMIUM', + HealthDataType.DIETARY_COPPER: 'DIETARY_COPPER', + HealthDataType.DIETARY_IODINE: 'DIETARY_IODINE', + HealthDataType.DIETARY_MANGANESE: 'DIETARY_MANGANESE', + HealthDataType.DIETARY_MOLYBDENUM: 'DIETARY_MOLYBDENUM', + HealthDataType.DIETARY_SELENIUM: 'DIETARY_SELENIUM', HealthDataType.FORCED_EXPIRATORY_VOLUME: 'FORCED_EXPIRATORY_VOLUME', HealthDataType.HEART_RATE: 'HEART_RATE', HealthDataType.HEART_RATE_VARIABILITY_SDNN: 'HEART_RATE_VARIABILITY_SDNN', @@ -532,13 +565,50 @@ const _$InsulinDeliveryReasonEnumMap = { NutritionHealthValue _$NutritionHealthValueFromJson( Map json) => NutritionHealthValue( + name: json['name'] as String?, mealType: json['meal_type'] as String?, - protein: (json['protein'] as num?)?.toDouble(), calories: (json['calories'] as num?)?.toDouble(), + protein: (json['protein'] as num?)?.toDouble(), fat: (json['fat'] as num?)?.toDouble(), - name: json['name'] as String?, carbs: (json['carbs'] as num?)?.toDouble(), caffeine: (json['caffeine'] as num?)?.toDouble(), + vitaminA: (json['vitamin_a'] as num?)?.toDouble(), + b1Thiamine: (json['b1_thiamine'] as num?)?.toDouble(), + b2Riboflavin: (json['b2_riboflavin'] as num?)?.toDouble(), + b3Niacin: (json['b3_niacin'] as num?)?.toDouble(), + b5PantothenicAcid: (json['b5_pantothenic_acid'] as num?)?.toDouble(), + b6Pyridoxine: (json['b6_pyridoxine'] as num?)?.toDouble(), + b7Biotin: (json['b7_biotin'] as num?)?.toDouble(), + b9Folate: (json['b9_folate'] as num?)?.toDouble(), + b12Cobalamin: (json['b12_cobalamin'] as num?)?.toDouble(), + vitaminC: (json['vitamin_c'] as num?)?.toDouble(), + vitaminD: (json['vitamin_d'] as num?)?.toDouble(), + vitaminE: (json['vitamin_e'] as num?)?.toDouble(), + vitaminK: (json['vitamin_k'] as num?)?.toDouble(), + calcium: (json['calcium'] as num?)?.toDouble(), + chloride: (json['chloride'] as num?)?.toDouble(), + cholesterol: (json['cholesterol'] as num?)?.toDouble(), + choline: (json['choline'] as num?)?.toDouble(), + chromium: (json['chromium'] as num?)?.toDouble(), + copper: (json['copper'] as num?)?.toDouble(), + fatUnsaturated: (json['fat_unsaturated'] as num?)?.toDouble(), + fatMonounsaturated: (json['fat_monounsaturated'] as num?)?.toDouble(), + fatPolyunsaturated: (json['fat_polyunsaturated'] as num?)?.toDouble(), + fatSaturated: (json['fat_saturated'] as num?)?.toDouble(), + fatTransMonoenoic: (json['fat_trans_monoenoic'] as num?)?.toDouble(), + fiber: (json['fiber'] as num?)?.toDouble(), + iodine: (json['iodine'] as num?)?.toDouble(), + iron: (json['iron'] as num?)?.toDouble(), + magnesium: (json['magnesium'] as num?)?.toDouble(), + manganese: (json['manganese'] as num?)?.toDouble(), + molybdenum: (json['molybdenum'] as num?)?.toDouble(), + phosphorus: (json['phosphorus'] as num?)?.toDouble(), + potassium: (json['potassium'] as num?)?.toDouble(), + selenium: (json['selenium'] as num?)?.toDouble(), + sodium: (json['sodium'] as num?)?.toDouble(), + sugar: (json['sugar'] as num?)?.toDouble(), + water: (json['water'] as num?)?.toDouble(), + zinc: (json['zinc'] as num?)?.toDouble(), )..$type = json['__type'] as String?; Map _$NutritionHealthValueToJson( @@ -552,13 +622,50 @@ Map _$NutritionHealthValueToJson( } writeNotNull('__type', instance.$type); + writeNotNull('name', instance.name); writeNotNull('meal_type', instance.mealType); - writeNotNull('protein', instance.protein); writeNotNull('calories', instance.calories); + writeNotNull('protein', instance.protein); writeNotNull('fat', instance.fat); - writeNotNull('name', instance.name); writeNotNull('carbs', instance.carbs); writeNotNull('caffeine', instance.caffeine); + writeNotNull('vitamin_a', instance.vitaminA); + writeNotNull('b1_thiamine', instance.b1Thiamine); + writeNotNull('b2_riboflavin', instance.b2Riboflavin); + writeNotNull('b3_niacin', instance.b3Niacin); + writeNotNull('b5_pantothenic_acid', instance.b5PantothenicAcid); + writeNotNull('b6_pyridoxine', instance.b6Pyridoxine); + writeNotNull('b7_biotin', instance.b7Biotin); + writeNotNull('b9_folate', instance.b9Folate); + writeNotNull('b12_cobalamin', instance.b12Cobalamin); + writeNotNull('vitamin_c', instance.vitaminC); + writeNotNull('vitamin_d', instance.vitaminD); + writeNotNull('vitamin_e', instance.vitaminE); + writeNotNull('vitamin_k', instance.vitaminK); + writeNotNull('calcium', instance.calcium); + writeNotNull('chloride', instance.chloride); + writeNotNull('cholesterol', instance.cholesterol); + writeNotNull('choline', instance.choline); + writeNotNull('chromium', instance.chromium); + writeNotNull('copper', instance.copper); + writeNotNull('fat_unsaturated', instance.fatUnsaturated); + writeNotNull('fat_monounsaturated', instance.fatMonounsaturated); + writeNotNull('fat_polyunsaturated', instance.fatPolyunsaturated); + writeNotNull('fat_saturated', instance.fatSaturated); + writeNotNull('fat_trans_monoenoic', instance.fatTransMonoenoic); + writeNotNull('fiber', instance.fiber); + writeNotNull('iodine', instance.iodine); + writeNotNull('iron', instance.iron); + writeNotNull('magnesium', instance.magnesium); + writeNotNull('manganese', instance.manganese); + writeNotNull('molybdenum', instance.molybdenum); + writeNotNull('phosphorus', instance.phosphorus); + writeNotNull('potassium', instance.potassium); + writeNotNull('selenium', instance.selenium); + writeNotNull('sodium', instance.sodium); + writeNotNull('sugar', instance.sugar); + writeNotNull('water', instance.water); + writeNotNull('zinc', instance.zinc); return val; } diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index f72c6cacc..b4228492b 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -517,11 +517,48 @@ class Health { /// It must be equal to or earlier than [endTime]. /// * [endTime] - the end time when the meal was consumed. /// It must be equal to or later than [startTime]. + /// * [name] - optional name information about this meal. /// * [caloriesConsumed] - total calories consumed with this meal. /// * [carbohydrates] - optional carbohydrates information. /// * [protein] - optional protein information. /// * [fatTotal] - optional total fat information. - /// * [name] - optional name information about this meal. + /// * [caffeine] - optional caffeine information. + /// * [vitaminA] - optional vitamin A information. + /// * [b1Thiamin] - optional vitamin B1 (thiamin) information. + /// * [b2Riboflavin] - optional vitamin B2 (riboflavin) information. + /// * [b3Niacin] - optional vitamin B3 (niacin) information. + /// * [b5PantothenicAcid] - optional vitamin B5 (pantothenic acid) information. + /// * [b6Pyridoxine] - optional vitamin B6 (pyridoxine) information. + /// * [b7Biotin] - optional vitamin B7 (biotin) information. + /// * [b9Folate] - optional vitamin B9 (folate) information. + /// * [b12Cobalamin] - optional vitamin B12 (cobalamin) information. + /// * [vitaminC] - optional vitamin C information. + /// * [vitaminD] - optional vitamin D information. + /// * [vitaminE] - optional vitamin E information. + /// * [vitaminK] - optional vitamin K information. + /// * [calcium] - optional calcium information. + /// * [cholesterol] - optional cholesterol information. + /// * [chloride] - optional chloride information. + /// * [chromium] - optional chromium information. + /// * [copper] - optional copper information. + /// * [fatUnsaturated] - optional unsaturated fat information. + /// * [fatMonounsaturated] - optional monounsaturated fat information. + /// * [fatPolyunsaturated] - optional polyunsaturated fat information. + /// * [fatSaturated] - optional saturated fat information. + /// * [fatTransMonoenoic] - optional trans-monoenoic fat information. + /// * [fiber] - optional fiber information. + /// * [iodine] - optional iodine information. + /// * [iron] - optional iron information. + /// * [magnesium] - optional magnesium information. + /// * [manganese] - optional manganese information. + /// * [molybdenum] - optional molybdenum information. + /// * [phosphorus] - optional phosphorus information. + /// * [potassium] - optional potassium information. + /// * [selenium] - optional selenium information. + /// * [sodium] - optional sodium information. + /// * [sugar] - optional sugar information. + /// * [water] - optional water information. + /// * [zinc] - optional zinc information. Future writeMeal({ required MealType mealType, required DateTime startTime, @@ -532,21 +569,93 @@ class Health { double? fatTotal, String? name, double? caffeine, + double? vitaminA, + double? b1Thiamin, + double? b2Riboflavin, + double? b3Niacin, + double? b5PantothenicAcid, + double? b6Pyridoxine, + double? b7Biotin, + double? b9Folate, + double? b12Cobalamin, + double? vitaminC, + double? vitaminD, + double? vitaminE, + double? vitaminK, + double? calcium, + double? cholesterol, + double? chloride, + double? chromium, + double? copper, + double? fatUnsaturated, + double? fatMonounsaturated, + double? fatPolyunsaturated, + double? fatSaturated, + double? fatTransMonoenoic, + double? fiber, + double? iodine, + double? iron, + double? magnesium, + double? manganese, + double? molybdenum, + double? phosphorus, + double? potassium, + double? selenium, + double? sodium, + double? sugar, + double? water, + double? zinc, }) async { if (startTime.isAfter(endTime)) { throw ArgumentError("startTime must be equal or earlier than endTime"); } Map args = { - 'startTime': startTime.millisecondsSinceEpoch, - 'endTime': endTime.millisecondsSinceEpoch, - 'caloriesConsumed': caloriesConsumed, - 'carbohydrates': carbohydrates, - 'protein': protein, - 'fatTotal': fatTotal, 'name': name, + 'meal_type': mealType.name, + 'start_time': startTime.millisecondsSinceEpoch, + 'end_time': endTime.millisecondsSinceEpoch, + 'calories': caloriesConsumed, + 'carbs': carbohydrates, + 'protein': protein, + 'fat': fatTotal, 'caffeine': caffeine, - 'mealType': mealType.name, + 'vitamin_a': vitaminA, + 'b1_thiamin': b1Thiamin, + 'b2_riboflavin': b2Riboflavin, + 'b3_niacin': b3Niacin, + 'b5_pantothenic_acid': b5PantothenicAcid, + 'b6_pyridoxine': b6Pyridoxine, + 'b7_biotin': b7Biotin, + 'b9_folate': b9Folate, + 'b12_cobalamin': b12Cobalamin, + 'vitamin_c': vitaminC, + 'vitamin_d': vitaminD, + 'vitamin_e': vitaminE, + 'vitamin_k': vitaminK, + 'calcium': calcium, + 'cholesterol': cholesterol, + 'chloride': chloride, + 'chromium': chromium, + 'copper': copper, + 'fat_unsaturated': fatUnsaturated, + 'fat_monounsaturated': fatMonounsaturated, + 'fat_polyunsaturated': fatPolyunsaturated, + 'fat_saturated': fatSaturated, + 'fat_trans_monoenoic': fatTransMonoenoic, + 'fiber': fiber, + 'iodine': iodine, + 'iron': iron, + 'magnesium': magnesium, + 'manganese': manganese, + 'molybdenum': molybdenum, + 'phosphorus': phosphorus, + 'potassium': potassium, + 'selenium': selenium, + 'sodium': sodium, + 'sugar': sugar, + 'water': water, + 'zinc': zinc, }; bool? success = await _channel.invokeMethod('writeMeal', args); return success ?? false; diff --git a/packages/health/lib/src/health_value_types.dart b/packages/health/lib/src/health_value_types.dart index 1e646f2b1..9c42ca6f8 100644 --- a/packages/health/lib/src/health_value_types.dart +++ b/packages/health/lib/src/health_value_types.dart @@ -373,44 +373,230 @@ class InsulinDeliveryHealthValue extends HealthValue { /// A [HealthValue] object for nutrition. /// /// Parameters: -/// * [protein] - the amount of protein in grams -/// * [calories] - the amount of calories in kcal -/// * [fat] - the amount of fat in grams +/// * [mealType] - the type of meal /// * [name] - the name of the food -/// * [carbs] - the amount of carbs in grams +/// * [b1Thiamine] - the amount of thiamine (B1) in grams +/// * [b2Riboflavin] - the amount of riboflavin (B2) in grams +/// * [b3Niacin] - the amount of niacin (B3) in grams +/// * [b5PantothenicAcid] - the amount of pantothenic acid (B5) in grams +/// * [b6Pyridoxine] - the amount of pyridoxine (B6) in grams +/// * [b7Biotin] - the amount of biotin (B7) in grams +/// * [b9Folate] - the amount of folate (B9) in grams +/// * [b12Cobalamin] - the amount of cobalamin (B12) in grams /// * [caffeine] - the amount of caffeine in grams -/// * [mealType] - the type of meal +/// * [calcium] - the amount of calcium in grams +/// * [calories] - the amount of calories in kcal +/// * [carbs] - the amount of carbs in grams +/// * [chloride] - the amount of chloride in grams +/// * [cholesterol] - the amount of cholesterol in grams +/// * [choline] - the amount of choline in grams +/// * [chromium] - the amount of chromium in grams +/// * [copper] - the amount of copper in grams +/// * [fat] - the amount of fat in grams +/// * [fatMonounsaturated] - the amount of monounsaturated fat in grams +/// * [fatPolyunsaturated] - the amount of polyunsaturated fat in grams +/// * [fatSaturated] - the amount of saturated fat in grams +/// * [fatTransMonoenoic] - the amount of +/// * [fatUnsaturated] - the amount of unsaturated fat in grams +/// * [fiber] - the amount of fiber in grams +/// * [iodine] - the amount of iodine in grams +/// * [iron] - the amount of iron in grams +/// * [magnesium] - the amount of magnesium in grams +/// * [manganese] - the amount of manganese in grams +/// * [molybdenum] - the amount of molybdenum in grams +/// * [phosphorus] - the amount of phosphorus in grams +/// * [potassium] - the amount of potassium in grams +/// * [protein] - the amount of protein in grams +/// * [selenium] - the amount of selenium in grams +/// * [sodium] - the amount of sodium in grams +/// * [sugar] - the amount of sugar in grams +/// * [vitaminA] - the amount of vitamin A in grams +/// * [vitaminC] - the amount of vitamin C in grams +/// * [vitaminD] - the amount of vitamin D in grams +/// * [vitaminE] - the amount of vitamin E in grams +/// * [vitaminK] - the amount of vitamin K in grams +/// * [water] - the amount of water in grams +/// * [zinc] - the amount of zinc in grams + @JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) class NutritionHealthValue extends HealthValue { + /// The name of the food. + String? name; + /// The type of meal. String? mealType; - /// The amount of protein in grams. - double? protein; - /// The amount of calories in kcal. double? calories; + /// The amount of protein in grams. + double? protein; + /// The amount of fat in grams. double? fat; - /// The name of the food. - String? name; - /// The amount of carbs in grams. double? carbs; /// The amount of caffeine in grams. double? caffeine; + /// The amount of vitamin A in grams. + double? vitaminA; + + /// The amount of thiamine (B1) in grams. + double? b1Thiamine; + + /// The amount of riboflavin (B2) in grams. + double? b2Riboflavin; + + /// The amount of niacin (B3) in grams. + double? b3Niacin; + + /// The amount of pantothenic acid (B5) in grams. + double? b5PantothenicAcid; + + /// The amount of pyridoxine (B6) in grams. + double? b6Pyridoxine; + + /// The amount of biotin (B7) in grams. + double? b7Biotin; + + /// The amount of folate (B9) in grams. + double? b9Folate; + + /// The amount of cobalamin (B12) in grams. + double? b12Cobalamin; + + /// The amount of vitamin C in grams. + double? vitaminC; + + /// The amount of vitamin D in grams. + double? vitaminD; + + /// The amount of vitamin E in grams. + double? vitaminE; + + /// The amount of vitamin K in grams. + double? vitaminK; + + /// The amount of calcium in grams. + double? calcium; + + /// The amount of chloride in grams. + double? chloride; + + /// The amount of cholesterol in grams. + double? cholesterol; + + /// The amount of choline in grams. + double? choline; + + /// The amount of chromium in grams. + double? chromium; + + /// The amount of copper in grams. + double? copper; + + /// The amount of unsaturated fat in grams. + double? fatUnsaturated; + + /// The amount of monounsaturated fat in grams. + double? fatMonounsaturated; + + /// The amount of polyunsaturated fat in grams. + double? fatPolyunsaturated; + + /// The amount of saturated fat in grams. + double? fatSaturated; + + /// The amount of trans-monoenoic fat in grams. + double? fatTransMonoenoic; + + /// The amount of fiber in grams. + double? fiber; + + /// The amount of iodine in grams. + double? iodine; + + /// The amount of iron in grams. + double? iron; + + /// The amount of magnesium in grams. + double? magnesium; + + /// The amount of manganese in grams. + double? manganese; + + /// The amount of molybdenum in grams. + double? molybdenum; + + /// The amount of phosphorus in grams. + double? phosphorus; + + /// The amount of potassium in grams. + double? potassium; + + /// The amount of selenium in grams. + double? selenium; + + /// The amount of sodium in grams. + double? sodium; + + /// The amount of sugar in grams. + double? sugar; + + /// The amount of water in grams. + double? water; + + /// The amount of zinc in grams. + double? zinc; + NutritionHealthValue({ + this.name, this.mealType, - this.protein, this.calories, + this.protein, this.fat, - this.name, this.carbs, this.caffeine, + this.vitaminA, + this.b1Thiamine, + this.b2Riboflavin, + this.b3Niacin, + this.b5PantothenicAcid, + this.b6Pyridoxine, + this.b7Biotin, + this.b9Folate, + this.b12Cobalamin, + this.vitaminC, + this.vitaminD, + this.vitaminE, + this.vitaminK, + this.calcium, + this.chloride, + this.cholesterol, + this.choline, + this.chromium, + this.copper, + this.fatUnsaturated, + this.fatMonounsaturated, + this.fatPolyunsaturated, + this.fatSaturated, + this.fatTransMonoenoic, + this.fiber, + this.iodine, + this.iron, + this.magnesium, + this.manganese, + this.molybdenum, + this.phosphorus, + this.potassium, + this.selenium, + this.sodium, + this.sugar, + this.water, + this.zinc, }); @override @@ -420,27 +606,17 @@ class NutritionHealthValue extends HealthValue { @override Map toJson() => _$NutritionHealthValueToJson(this); + static double? _toDoubleOrNull(num? value) => value?.toDouble(); + /// Create a [NutritionHealthValue] based on a health data point from native data format. - factory NutritionHealthValue.fromHealthDataPoint(dynamic dataPoint) => - NutritionHealthValue( - mealType: dataPoint['mealType'] as String, - protein: dataPoint['protein'] != null - ? (dataPoint['protein'] as num).toDouble() - : null, - calories: dataPoint['calories'] != null - ? (dataPoint['calories'] as num).toDouble() - : null, - fat: dataPoint['fat'] != null - ? (dataPoint['fat'] as num).toDouble() - : null, - name: dataPoint['name'] != null ? (dataPoint['name'] as String) : null, - carbs: dataPoint['carbs'] != null - ? (dataPoint['carbs'] as num).toDouble() - : null, - caffeine: dataPoint['caffeine'] != null - ? (dataPoint['caffeine'] as num).toDouble() - : null, - ); + factory NutritionHealthValue.fromHealthDataPoint(dynamic dataPoint) { + dataPoint = dataPoint as Map; + // where key is not null + final Map dataPointMap = Map.fromEntries(dataPoint.entries + .where((entry) => entry.key != null) + .map((entry) => MapEntry(entry.key as String, entry.value))); + return _$NutritionHealthValueFromJson(dataPointMap); + } @override String toString() => """$runtimeType - protein: ${protein.toString()}, @@ -449,20 +625,137 @@ class NutritionHealthValue extends HealthValue { name: ${name.toString()}, carbs: ${carbs.toString()}, caffeine: ${caffeine.toString()}, - mealType: $mealType"""; + mealType: $mealType, + vitaminA: ${vitaminA.toString()}, + b1Thiamine: ${b1Thiamine.toString()}, + b2Riboflavin: ${b2Riboflavin.toString()}, + b3Niacin: ${b3Niacin.toString()}, + b5PantothenicAcid: ${b5PantothenicAcid.toString()}, + b6Pyridoxine: ${b6Pyridoxine.toString()}, + b7Biotin: ${b7Biotin.toString()}, + b9Folate: ${b9Folate.toString()}, + b12Cobalamin: ${b12Cobalamin.toString()}, + vitaminC: ${vitaminC.toString()}, + vitaminD: ${vitaminD.toString()}, + vitaminE: ${vitaminE.toString()}, + vitaminK: ${vitaminK.toString()}, + calcium: ${calcium.toString()}, + chloride: ${chloride.toString()}, + cholesterol: ${cholesterol.toString()}, + choline: ${choline.toString()}, + chromium: ${chromium.toString()}, + copper: ${copper.toString()}, + unsaturatedFat: ${fatUnsaturated.toString()}, + fatMonounsaturated: ${fatMonounsaturated.toString()}, + fatPolyunsaturated: ${fatPolyunsaturated.toString()}, + fatSaturated: ${fatSaturated.toString()}, + fatTransMonoenoic: ${fatTransMonoenoic.toString()}, + fiber: ${fiber.toString()}, + iodine: ${iodine.toString()}, + iron: ${iron.toString()}, + magnesium: ${magnesium.toString()}, + manganese: ${manganese.toString()}, + molybdenum: ${molybdenum.toString()}, + phosphorus: ${phosphorus.toString()}, + potassium: ${potassium.toString()}, + selenium: ${selenium.toString()}, + sodium: ${sodium.toString()}, + sugar: ${sugar.toString()}, + water: ${water.toString()}, + zinc: ${zinc.toString()}"""; @override bool operator ==(Object other) => other is NutritionHealthValue && - other.protein == protein && + other.name == name && + other.mealType == mealType && other.calories == calories && + other.protein == protein && other.fat == fat && - other.name == name && other.carbs == carbs && other.caffeine == caffeine && - other.mealType == mealType; - - @override - int get hashCode => - Object.hash(protein, calories, fat, name, carbs, caffeine); + other.vitaminA == vitaminA && + other.b1Thiamine == b1Thiamine && + other.b2Riboflavin == b2Riboflavin && + other.b3Niacin == b3Niacin && + other.b5PantothenicAcid == b5PantothenicAcid && + other.b6Pyridoxine == b6Pyridoxine && + other.b7Biotin == b7Biotin && + other.b9Folate == b9Folate && + other.b12Cobalamin == b12Cobalamin && + other.vitaminC == vitaminC && + other.vitaminD == vitaminD && + other.vitaminE == vitaminE && + other.vitaminK == vitaminK && + other.calcium == calcium && + other.chloride == chloride && + other.cholesterol == cholesterol && + other.choline == choline && + other.chromium == chromium && + other.copper == copper && + other.fatUnsaturated == fatUnsaturated && + other.fatMonounsaturated == fatMonounsaturated && + other.fatPolyunsaturated == fatPolyunsaturated && + other.fatSaturated == fatSaturated && + other.fatTransMonoenoic == fatTransMonoenoic && + other.fiber == fiber && + other.iodine == iodine && + other.iron == iron && + other.magnesium == magnesium && + other.manganese == manganese && + other.molybdenum == molybdenum && + other.phosphorus == phosphorus && + other.potassium == potassium && + other.selenium == selenium && + other.sodium == sodium && + other.sugar == sugar && + other.water == water && + other.zinc == zinc; + + @override + int get hashCode => Object.hashAll([ + protein, + calories, + fat, + name, + carbs, + caffeine, + vitaminA, + b1Thiamine, + b2Riboflavin, + b3Niacin, + b5PantothenicAcid, + b6Pyridoxine, + b7Biotin, + b9Folate, + b12Cobalamin, + vitaminC, + vitaminD, + vitaminE, + vitaminK, + calcium, + chloride, + cholesterol, + choline, + chromium, + copper, + fatUnsaturated, + fatMonounsaturated, + fatPolyunsaturated, + fatSaturated, + fatTransMonoenoic, + fiber, + iodine, + iron, + magnesium, + manganese, + molybdenum, + phosphorus, + potassium, + selenium, + sodium, + sugar, + water, + zinc, + ]); } diff --git a/packages/health/lib/src/heath_data_types.dart b/packages/health/lib/src/heath_data_types.dart index 28caa58f0..c8994cae2 100644 --- a/packages/health/lib/src/heath_data_types.dart +++ b/packages/health/lib/src/heath_data_types.dart @@ -18,6 +18,39 @@ enum HealthDataType { DIETARY_ENERGY_CONSUMED, DIETARY_FATS_CONSUMED, DIETARY_PROTEIN_CONSUMED, + DIETARY_FIBER, + DIETARY_SUGAR, + DIETARY_FAT_MONOUNSATURATED, + DIETARY_FAT_POLYUNSATURATED, + DIETARY_FAT_SATURATED, + DIETARY_CHOLESTEROL, + DIETARY_VITAMIN_A, + DIETARY_THIAMIN, + DIETARY_RIBOFLAVIN, + DIETARY_NIACIN, + DIETARY_PANTOTHENIC_ACID, + DIETARY_VITAMIN_B6, + DIETARY_BIOTIN, + DIETARY_VITAMIN_B12, + DIETARY_VITAMIN_C, + DIETARY_VITAMIN_D, + DIETARY_VITAMIN_E, + DIETARY_VITAMIN_K, + DIETARY_FOLATE, + DIETARY_CALCIUM, + DIETARY_CHLORIDE, + DIETARY_IRON, + DIETARY_MAGNESIUM, + DIETARY_PHOSPHORUS, + DIETARY_POTASSIUM, + DIETARY_SODIUM, + DIETARY_ZINC, + DIETARY_CHROMIUM, + DIETARY_COPPER, + DIETARY_IODINE, + DIETARY_MANGANESE, + DIETARY_MOLYBDENUM, + DIETARY_SELENIUM, FORCED_EXPIRATORY_VOLUME, HEART_RATE, HEART_RATE_VARIABILITY_SDNN, @@ -93,6 +126,39 @@ const List dataTypeKeysIOS = [ HealthDataType.DIETARY_ENERGY_CONSUMED, HealthDataType.DIETARY_FATS_CONSUMED, HealthDataType.DIETARY_PROTEIN_CONSUMED, + HealthDataType.DIETARY_FIBER, + HealthDataType.DIETARY_SUGAR, + HealthDataType.DIETARY_FAT_MONOUNSATURATED, + HealthDataType.DIETARY_FAT_POLYUNSATURATED, + HealthDataType.DIETARY_FAT_SATURATED, + HealthDataType.DIETARY_CHOLESTEROL, + HealthDataType.DIETARY_VITAMIN_A, + HealthDataType.DIETARY_THIAMIN, + HealthDataType.DIETARY_RIBOFLAVIN, + HealthDataType.DIETARY_NIACIN, + HealthDataType.DIETARY_PANTOTHENIC_ACID, + HealthDataType.DIETARY_VITAMIN_B6, + HealthDataType.DIETARY_BIOTIN, + HealthDataType.DIETARY_VITAMIN_B12, + HealthDataType.DIETARY_VITAMIN_C, + HealthDataType.DIETARY_VITAMIN_D, + HealthDataType.DIETARY_VITAMIN_E, + HealthDataType.DIETARY_VITAMIN_K, + HealthDataType.DIETARY_FOLATE, + HealthDataType.DIETARY_CALCIUM, + HealthDataType.DIETARY_CHLORIDE, + HealthDataType.DIETARY_IRON, + HealthDataType.DIETARY_MAGNESIUM, + HealthDataType.DIETARY_PHOSPHORUS, + HealthDataType.DIETARY_POTASSIUM, + HealthDataType.DIETARY_SODIUM, + HealthDataType.DIETARY_ZINC, + HealthDataType.DIETARY_CHROMIUM, + HealthDataType.DIETARY_COPPER, + HealthDataType.DIETARY_IODINE, + HealthDataType.DIETARY_MANGANESE, + HealthDataType.DIETARY_MOLYBDENUM, + HealthDataType.DIETARY_SELENIUM, HealthDataType.ELECTRODERMAL_ACTIVITY, HealthDataType.FORCED_EXPIRATORY_VOLUME, HealthDataType.HEART_RATE, @@ -187,6 +253,40 @@ const Map dataTypeToUnit = { HealthDataType.DIETARY_ENERGY_CONSUMED: HealthDataUnit.KILOCALORIE, HealthDataType.DIETARY_FATS_CONSUMED: HealthDataUnit.GRAM, HealthDataType.DIETARY_PROTEIN_CONSUMED: HealthDataUnit.GRAM, + HealthDataType.DIETARY_FIBER: HealthDataUnit.GRAM, + HealthDataType.DIETARY_SUGAR: HealthDataUnit.GRAM, + HealthDataType.DIETARY_FAT_MONOUNSATURATED: HealthDataUnit.GRAM, + HealthDataType.DIETARY_FAT_POLYUNSATURATED: HealthDataUnit.GRAM, + HealthDataType.DIETARY_FAT_SATURATED: HealthDataUnit.GRAM, + HealthDataType.DIETARY_CHOLESTEROL: HealthDataUnit.GRAM, + HealthDataType.DIETARY_VITAMIN_A: HealthDataUnit.GRAM, + HealthDataType.DIETARY_THIAMIN: HealthDataUnit.GRAM, + HealthDataType.DIETARY_RIBOFLAVIN: HealthDataUnit.GRAM, + HealthDataType.DIETARY_NIACIN: HealthDataUnit.GRAM, + HealthDataType.DIETARY_PANTOTHENIC_ACID: HealthDataUnit.GRAM, + HealthDataType.DIETARY_VITAMIN_B6: HealthDataUnit.GRAM, + HealthDataType.DIETARY_BIOTIN: HealthDataUnit.GRAM, + HealthDataType.DIETARY_VITAMIN_B12: HealthDataUnit.GRAM, + HealthDataType.DIETARY_VITAMIN_C: HealthDataUnit.GRAM, + HealthDataType.DIETARY_VITAMIN_D: HealthDataUnit.GRAM, + HealthDataType.DIETARY_VITAMIN_E: HealthDataUnit.GRAM, + HealthDataType.DIETARY_VITAMIN_K: HealthDataUnit.GRAM, + HealthDataType.DIETARY_FOLATE: HealthDataUnit.GRAM, + HealthDataType.DIETARY_CALCIUM: HealthDataUnit.GRAM, + HealthDataType.DIETARY_CHLORIDE: HealthDataUnit.GRAM, + HealthDataType.DIETARY_IRON: HealthDataUnit.GRAM, + HealthDataType.DIETARY_MAGNESIUM: HealthDataUnit.GRAM, + HealthDataType.DIETARY_PHOSPHORUS: HealthDataUnit.GRAM, + HealthDataType.DIETARY_POTASSIUM: HealthDataUnit.GRAM, + HealthDataType.DIETARY_SODIUM: HealthDataUnit.GRAM, + HealthDataType.DIETARY_ZINC: HealthDataUnit.GRAM, + HealthDataType.DIETARY_CHROMIUM: HealthDataUnit.GRAM, + HealthDataType.DIETARY_COPPER: HealthDataUnit.GRAM, + HealthDataType.DIETARY_IODINE: HealthDataUnit.GRAM, + HealthDataType.DIETARY_MANGANESE: HealthDataUnit.GRAM, + HealthDataType.DIETARY_MOLYBDENUM: HealthDataUnit.GRAM, + HealthDataType.DIETARY_SELENIUM: HealthDataUnit.GRAM, + HealthDataType.ELECTRODERMAL_ACTIVITY: HealthDataUnit.SIEMEN, HealthDataType.FORCED_EXPIRATORY_VOLUME: HealthDataUnit.LITER, HealthDataType.HEART_RATE: HealthDataUnit.BEATS_PER_MINUTE, From a77408b4bddb0cd6cbfd48a9f69dcaf39b8061e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Bardo=C5=88?= <32016375+mikeb2k@users.noreply.github.com> Date: Tue, 30 Jul 2024 13:54:54 +0200 Subject: [PATCH 12/80] Build Gradle: Add namespace (#1002) --- packages/light/android/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/light/android/build.gradle b/packages/light/android/build.gradle index 0d2c15b97..d256b7247 100644 --- a/packages/light/android/build.gradle +++ b/packages/light/android/build.gradle @@ -26,6 +26,7 @@ apply plugin: 'kotlin-android' android { compileSdkVersion 31 + namespace 'dk.cachet.light' compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 From 815fc6f197a976353d00f3986e4e02221985ecdd Mon Sep 17 00:00:00 2001 From: Luan Silva <133707335+luansilva-tokenlab@users.noreply.github.com> Date: Tue, 30 Jul 2024 14:48:42 -0300 Subject: [PATCH 13/80] [screen_state] iOS support and renewal of the native example apps (#901) --- README.md | 2 +- packages/screen_state/.fvmrc | 3 + packages/screen_state/.gitignore | 3 + packages/screen_state/CHANGELOG.md | 5 + packages/screen_state/README.md | 8 +- packages/screen_state/example/.gitignore | 43 +++ packages/screen_state/example/.metadata | 39 ++- .../example/analysis_options.yaml | 28 ++ .../screen_state/example/android/.gitignore | 13 + .../example/android/app/build.gradle | 42 +-- .../android/app/src/debug/AndroidManifest.xml | 6 +- .../android/app/src/main/AndroidManifest.xml | 21 +- .../com/example/example/MainActivity.kt | 6 + .../res/drawable-v21/launch_background.xml | 12 + .../app/src/main/res/values-night/styles.xml | 18 ++ .../app/src/main/res/values/styles.xml | 12 +- .../app/src/profile/AndroidManifest.xml | 6 +- .../screen_state/example/android/build.gradle | 7 +- .../example/android/gradle.properties | 3 +- .../android/gradle/wrapper/gradle-wrapper.jar | Bin 59821 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 2 +- packages/screen_state/example/android/gradlew | 234 -------------- .../screen_state/example/android/gradlew.bat | 89 ------ .../example/android/settings.gradle | 36 ++- packages/screen_state/example/ios/.gitignore | 34 ++ .../ios/Flutter/AppFrameworkInfo.plist | 4 +- .../example/ios/Flutter/Debug.xcconfig | 2 +- .../example/ios/Flutter/Release.xcconfig | 2 +- packages/screen_state/example/ios/Podfile | 79 ++--- .../ios/Runner.xcodeproj/project.pbxproj | 293 +++++++++++++++--- .../contents.xcworkspacedata | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 21 +- .../contents.xcworkspacedata | 3 + .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 564 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 1283 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 1588 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 1025 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 1716 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 1920 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 1283 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 1895 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 2665 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 2665 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 3831 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 1888 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 3294 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 3612 -> 1418 bytes .../example/ios/Runner/Info.plist | 10 +- .../example/ios/RunnerTests/RunnerTests.swift | 12 + packages/screen_state/example/pubspec.yaml | 57 +--- .../ios/Classes/SwiftScreenStatePlugin.swift | 83 ++++- .../screen_state/ios/screen_state.podspec | 10 +- packages/screen_state/lib/screen_state.dart | 81 +++-- packages/screen_state/pubspec.yaml | 4 +- 54 files changed, 742 insertions(+), 593 deletions(-) create mode 100644 packages/screen_state/.fvmrc create mode 100644 packages/screen_state/.gitignore create mode 100644 packages/screen_state/example/.gitignore create mode 100644 packages/screen_state/example/analysis_options.yaml create mode 100644 packages/screen_state/example/android/.gitignore create mode 100644 packages/screen_state/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt create mode 100644 packages/screen_state/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 packages/screen_state/example/android/app/src/main/res/values-night/styles.xml delete mode 100644 packages/screen_state/example/android/gradle/wrapper/gradle-wrapper.jar delete mode 100755 packages/screen_state/example/android/gradlew delete mode 100644 packages/screen_state/example/android/gradlew.bat create mode 100644 packages/screen_state/example/ios/.gitignore create mode 100644 packages/screen_state/example/ios/RunnerTests/RunnerTests.swift diff --git a/README.md b/README.md index 548cf3bb3..5326c9bf4 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ These are the available plugins in this repository. | Plugin | Description | Android | iOS | http://pub.dev/ | |--------|-------------|:-------:|:---:|:---------:| -| [screen_state](./packages/screen_state) | Track screen state changes | ✔️ | ❌ | [![pub package](https://img.shields.io/pub/v/screen_state.svg)](https://pub.dartlang.org/packages/screen_state) | +| [screen_state](./packages/screen_state) | Track screen state changes | ✔️ | ✔️ | [![pub package](https://img.shields.io/pub/v/screen_state.svg)](https://pub.dartlang.org/packages/screen_state) | | [light](./packages/light) | Track light sensor readings | ✔️ | ❌ | [![pub package](https://img.shields.io/pub/v/light.svg)](https://pub.dartlang.org/packages/light) | | [pedometer](./packages/pedometer) | Track step count | ✔️ | ✔️ | [![pub package](https://img.shields.io/pub/v/pedometer.svg)](https://pub.dartlang.org/packages/pedometer) | | [noise_meter](./packages/noise_meter) | Read noise level in Decibel | ✔️ | ✔️ | [![pub package](https://img.shields.io/pub/v/noise_meter.svg)](https://pub.dartlang.org/packages/noise_meter) | diff --git a/packages/screen_state/.fvmrc b/packages/screen_state/.fvmrc new file mode 100644 index 000000000..c300356c3 --- /dev/null +++ b/packages/screen_state/.fvmrc @@ -0,0 +1,3 @@ +{ + "flutter": "stable" +} \ No newline at end of file diff --git a/packages/screen_state/.gitignore b/packages/screen_state/.gitignore new file mode 100644 index 000000000..9e366fe3b --- /dev/null +++ b/packages/screen_state/.gitignore @@ -0,0 +1,3 @@ + +# FVM Version Cache +.fvm/ \ No newline at end of file diff --git a/packages/screen_state/CHANGELOG.md b/packages/screen_state/CHANGELOG.md index 2d0b19d97..cac4d1f1a 100644 --- a/packages/screen_state/CHANGELOG.md +++ b/packages/screen_state/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.1.0 + +- Added support for iOS. +- Restructuring of the interaction between the plugin and native applications. + ## 3.0.1 - Reduced minSdk version to 23 diff --git a/packages/screen_state/README.md b/packages/screen_state/README.md index 2266df28b..7bd024baa 100644 --- a/packages/screen_state/README.md +++ b/packages/screen_state/README.md @@ -36,4 +36,10 @@ The stream can also be cancelled again by calling the `cancel()` method: void stopListening() { _subscription.cancel(); } -``` \ No newline at end of file +``` + +## Limitations + +#### iOS: +- This package will exclusively detect screen unlocks on iOS devices that are protected with a passcode. If the device lacks a passcode, it will solely detect the screen's on/off state; +- The detection doesn't function when the app is running in the iOS simulator. The plugin will only return a 'Screen_On' event. \ No newline at end of file diff --git a/packages/screen_state/example/.gitignore b/packages/screen_state/example/.gitignore new file mode 100644 index 000000000..29a3a5017 --- /dev/null +++ b/packages/screen_state/example/.gitignore @@ -0,0 +1,43 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/packages/screen_state/example/.metadata b/packages/screen_state/example/.metadata index ade6bc987..b5c370f38 100644 --- a/packages/screen_state/example/.metadata +++ b/packages/screen_state/example/.metadata @@ -4,7 +4,42 @@ # This file should be version controlled and should not be manually edited. version: - revision: 8af6b2f038c1172e61d418869363a28dffec3cb4 - channel: stable + revision: "41456452f29d64e8deb623a3c927524bcf9f111b" + channel: "stable" project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + - platform: android + create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + - platform: ios + create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + - platform: linux + create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + - platform: macos + create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + - platform: web + create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + - platform: windows + create_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + base_revision: 41456452f29d64e8deb623a3c927524bcf9f111b + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/screen_state/example/analysis_options.yaml b/packages/screen_state/example/analysis_options.yaml new file mode 100644 index 000000000..0d2902135 --- /dev/null +++ b/packages/screen_state/example/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/screen_state/example/android/.gitignore b/packages/screen_state/example/android/.gitignore new file mode 100644 index 000000000..6f568019d --- /dev/null +++ b/packages/screen_state/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/packages/screen_state/example/android/app/build.gradle b/packages/screen_state/example/android/app/build.gradle index 40d90dec2..3578f3e4f 100644 --- a/packages/screen_state/example/android/app/build.gradle +++ b/packages/screen_state/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,26 +22,31 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { - compileSdkVersion 33 + namespace "com.example.example" + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion - sourceSets { - main.java.srcDirs += 'src/main/kotlin' + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' } - lintOptions { - disable 'InvalidPackage' + sourceSets { + main.java.srcDirs += 'src/main/kotlin' } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "dk.cachet.screen_state_example" + applicationId "com.example.example" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion 23 - targetSdkVersion 33 + targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName } @@ -58,6 +64,4 @@ flutter { source '../..' } -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} +dependencies {} diff --git a/packages/screen_state/example/android/app/src/debug/AndroidManifest.xml b/packages/screen_state/example/android/app/src/debug/AndroidManifest.xml index a03c35b31..399f6981d 100644 --- a/packages/screen_state/example/android/app/src/debug/AndroidManifest.xml +++ b/packages/screen_state/example/android/app/src/debug/AndroidManifest.xml @@ -1,6 +1,6 @@ - - diff --git a/packages/screen_state/example/android/app/src/main/AndroidManifest.xml b/packages/screen_state/example/android/app/src/main/AndroidManifest.xml index 84a90baf1..2710e5dc7 100644 --- a/packages/screen_state/example/android/app/src/main/AndroidManifest.xml +++ b/packages/screen_state/example/android/app/src/main/AndroidManifest.xml @@ -1,16 +1,10 @@ - - + - - diff --git a/packages/screen_state/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/packages/screen_state/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt new file mode 100644 index 000000000..e793a000d --- /dev/null +++ b/packages/screen_state/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/packages/screen_state/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/screen_state/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000..f74085f3f --- /dev/null +++ b/packages/screen_state/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/screen_state/example/android/app/src/main/res/values-night/styles.xml b/packages/screen_state/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..06952be74 --- /dev/null +++ b/packages/screen_state/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/screen_state/example/android/app/src/main/res/values/styles.xml b/packages/screen_state/example/android/app/src/main/res/values/styles.xml index 1f83a33fd..cb1ef8805 100644 --- a/packages/screen_state/example/android/app/src/main/res/values/styles.xml +++ b/packages/screen_state/example/android/app/src/main/res/values/styles.xml @@ -1,18 +1,18 @@ - - - diff --git a/packages/screen_state/example/android/app/src/profile/AndroidManifest.xml b/packages/screen_state/example/android/app/src/profile/AndroidManifest.xml index a03c35b31..399f6981d 100644 --- a/packages/screen_state/example/android/app/src/profile/AndroidManifest.xml +++ b/packages/screen_state/example/android/app/src/profile/AndroidManifest.xml @@ -1,6 +1,6 @@ - - diff --git a/packages/screen_state/example/android/build.gradle b/packages/screen_state/example/android/build.gradle index dd00b8ec8..e83fb5dac 100644 --- a/packages/screen_state/example/android/build.gradle +++ b/packages/screen_state/example/android/build.gradle @@ -1,12 +1,11 @@ buildscript { - ext.kotlin_version = '1.7.20' + ext.kotlin_version = '1.7.10' repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.3.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -14,7 +13,7 @@ buildscript { allprojects { repositories { google() - jcenter() + mavenCentral() } } diff --git a/packages/screen_state/example/android/gradle.properties b/packages/screen_state/example/android/gradle.properties index 38c8d4544..598d13fee 100644 --- a/packages/screen_state/example/android/gradle.properties +++ b/packages/screen_state/example/android/gradle.properties @@ -1,4 +1,3 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true +org.gradle.jvmargs=-Xmx4G android.useAndroidX=true android.enableJetifier=true diff --git a/packages/screen_state/example/android/gradle/wrapper/gradle-wrapper.jar b/packages/screen_state/example/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 41d9927a4d4fb3f96a785543079b8df6723c946b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59821 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 diff --git a/packages/screen_state/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/screen_state/example/android/gradle/wrapper/gradle-wrapper.properties index 774fae876..3c472b99c 100644 --- a/packages/screen_state/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/screen_state/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/packages/screen_state/example/android/gradlew b/packages/screen_state/example/android/gradlew deleted file mode 100755 index 1b6c78733..000000000 --- a/packages/screen_state/example/android/gradlew +++ /dev/null @@ -1,234 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/packages/screen_state/example/android/gradlew.bat b/packages/screen_state/example/android/gradlew.bat deleted file mode 100644 index 107acd32c..000000000 --- a/packages/screen_state/example/android/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/packages/screen_state/example/android/settings.gradle b/packages/screen_state/example/android/settings.gradle index d3b6a4013..7cd712855 100644 --- a/packages/screen_state/example/android/settings.gradle +++ b/packages/screen_state/example/android/settings.gradle @@ -1,15 +1,29 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + } + settings.ext.flutterSdkPath = flutterSdkPath() -include ':app' + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + repositories { + google() + mavenCentral() + gradlePluginPortal() + } -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + plugins { + id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false + } +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false +} + +include ":app" diff --git a/packages/screen_state/example/ios/.gitignore b/packages/screen_state/example/ios/.gitignore new file mode 100644 index 000000000..7a7f9873a --- /dev/null +++ b/packages/screen_state/example/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/packages/screen_state/example/ios/Flutter/AppFrameworkInfo.plist b/packages/screen_state/example/ios/Flutter/AppFrameworkInfo.plist index 6b4c0f78a..7c5696400 100644 --- a/packages/screen_state/example/ios/Flutter/AppFrameworkInfo.plist +++ b/packages/screen_state/example/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) + en CFBundleExecutable App CFBundleIdentifier @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 12.0 diff --git a/packages/screen_state/example/ios/Flutter/Debug.xcconfig b/packages/screen_state/example/ios/Flutter/Debug.xcconfig index e8efba114..ec97fc6f3 100644 --- a/packages/screen_state/example/ios/Flutter/Debug.xcconfig +++ b/packages/screen_state/example/ios/Flutter/Debug.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/packages/screen_state/example/ios/Flutter/Release.xcconfig b/packages/screen_state/example/ios/Flutter/Release.xcconfig index 399e9340e..c4855bfe2 100644 --- a/packages/screen_state/example/ios/Flutter/Release.xcconfig +++ b/packages/screen_state/example/ios/Flutter/Release.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/packages/screen_state/example/ios/Podfile b/packages/screen_state/example/ios/Podfile index 6697f0a53..d97f17e22 100644 --- a/packages/screen_state/example/ios/Podfile +++ b/packages/screen_state/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -10,78 +10,35 @@ project 'Runner', { 'Release' => :release, } -def parse_KV_file(file, separator='=') - file_abs_path = File.expand_path(file) - if !File.exists? file_abs_path - return []; +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" end - generated_key_values = {} - skip_line_start_symbols = ["#", "/"] - File.foreach(file_abs_path) do |line| - next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } - plugin = line.split(pattern=separator) - if plugin.length == 2 - podname = plugin[0].strip() - path = plugin[1].strip() - podpath = File.expand_path("#{path}", file_abs_path) - generated_key_values[podname] = podpath - else - puts "Invalid plugin specification: #{line}" - end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches end - generated_key_values + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" end +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + target 'Runner' do use_frameworks! use_modular_headers! - # Flutter Pod - - copied_flutter_dir = File.join(__dir__, 'Flutter') - copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') - copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') - unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) - # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. - # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. - # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. - - generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') - unless File.exist?(generated_xcode_build_settings_path) - raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) - cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; - - unless File.exist?(copied_framework_path) - FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) - end - unless File.exist?(copied_podspec_path) - FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) - end - end - - # Keep pod path relative so it can be checked into Podfile.lock. - pod 'Flutter', :path => 'Flutter' - - # Plugin Pods - - # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock - # referring to absolute paths on developers' machines. - system('rm -rf .symlinks') - system('mkdir -p .symlinks/plugins') - plugin_pods = parse_KV_file('../.flutter-plugins') - plugin_pods.each do |name, path| - symlink = File.join('.symlinks', 'plugins', name) - File.symlink(path, symlink) - pod name, :path => File.join(symlink, 'ios') + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths end end post_install do |installer| installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['ENABLE_BITCODE'] = 'NO' - end + flutter_additional_ios_build_settings(target) end end diff --git a/packages/screen_state/example/ios/Runner.xcodeproj/project.pbxproj b/packages/screen_state/example/ios/Runner.xcodeproj/project.pbxproj index e02e926a0..0548f766b 100644 --- a/packages/screen_state/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/screen_state/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,18 +3,31 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 32C83F22EDB013A5A3756E58 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76D046AA19DFF8C399B250FC /* Pods_RunnerTests.framework */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 8B3B6985881CA6E5CC4A08D3 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4780D8A493E440DD9CF363D /* Pods_Runner.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -31,9 +44,13 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 52BA1706B5253876613D90E4 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 76D046AA19DFF8C399B250FC /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; @@ -42,19 +59,55 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CA3E07307946F57CBC6E8477 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + D2F5F5B0D77CEED366D0877D /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + D40FEC992E5A86050ADA9172 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + E4780D8A493E440DD9CF363D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F11AFF197ED0A0DD0101AA8D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + F1E8B89737BE45CF520BDBE7 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 07B45BB3578FB808A88D1625 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 32C83F22EDB013A5A3756E58 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8B3B6985881CA6E5CC4A08D3 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 508FFBD0B3137313E52A90E3 /* Pods */ = { + isa = PBXGroup; + children = ( + D40FEC992E5A86050ADA9172 /* Pods-Runner.debug.xcconfig */, + 52BA1706B5253876613D90E4 /* Pods-Runner.release.xcconfig */, + F11AFF197ED0A0DD0101AA8D /* Pods-Runner.profile.xcconfig */, + CA3E07307946F57CBC6E8477 /* Pods-RunnerTests.debug.xcconfig */, + D2F5F5B0D77CEED366D0877D /* Pods-RunnerTests.release.xcconfig */, + F1E8B89737BE45CF520BDBE7 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -72,6 +125,9 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + 508FFBD0B3137313E52A90E3 /* Pods */, + FBF20B9D02BE7523F4E44B50 /* Frameworks */, ); sourceTree = ""; }; @@ -79,6 +135,7 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -90,7 +147,6 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, @@ -99,26 +155,49 @@ path = Runner; sourceTree = ""; }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { + FBF20B9D02BE7523F4E44B50 /* Frameworks */ = { isa = PBXGroup; children = ( + E4780D8A493E440DD9CF363D /* Pods_Runner.framework */, + 76D046AA19DFF8C399B250FC /* Pods_RunnerTests.framework */, ); - name = "Supporting Files"; + name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + B55B8D965C6E6048224C47A5 /* [CP] Check Pods Manifest.lock */, + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + 07B45BB3578FB808A88D1625 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + F2C1791CFE3305550046F77E /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 7E088B4E60C7BF58505CCD92 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -135,9 +214,14 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; LastSwiftMigration = 1100; @@ -158,11 +242,19 @@ projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -179,10 +271,12 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -191,8 +285,26 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 7E088B4E60C7BF58505CCD92 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -205,9 +317,61 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + B55B8D965C6E6048224C47A5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + 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; + }; + F2C1791CFE3305550046F77E /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + 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; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EA1CF9000F007C117D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -219,6 +383,14 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -241,7 +413,6 @@ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -281,7 +452,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -298,17 +469,14 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + INFOPLIST_KEY_CFBundleDisplayName = "Screen State Example"; + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = dk.cachet.screenStateExample; + MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -316,9 +484,58 @@ }; name = Profile; }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = CA3E07307946F57CBC6E8477 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D2F5F5B0D77CEED366D0877D /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F1E8B89737BE45CF520BDBE7 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -364,7 +581,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -374,7 +591,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -414,11 +630,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -432,17 +649,14 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + INFOPLIST_KEY_CFBundleDisplayName = "Screen State Example"; + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = dk.cachet.screenStateExample; + MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -459,17 +673,14 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + INFOPLIST_KEY_CFBundleDisplayName = "Screen State Example"; + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = dk.cachet.screenStateExample; + MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -480,6 +691,16 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/packages/screen_state/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/screen_state/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a16e..919434a62 100644 --- a/packages/screen_state/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/packages/screen_state/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/packages/screen_state/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/screen_state/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140cfd..8e3ca5dfe 100644 --- a/packages/screen_state/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/screen_state/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ - - - - + + + + + + - - + + diff --git a/packages/screen_state/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/screen_state/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index 28c6bf03016f6c994b70f38d1b7346e5831b531f..7353c41ecf9ca08017312dc233d9830079b50717 100644 GIT binary patch delta 279 zcmV+y0qFj;1g8R!8Gi!+006pI?LPnj0Blf9R7L;)|5U~J`u_j-{Qm)0oAmqtj@kOz z^8J|I`-|B6ht~R5kG+%I`zf~eztraM`u^bc{`dO)zUlmg)%x%C`E}6wSI77~z4s`y z^XT{f(eM4n?EUff`e@AgO~UxV*5*r_%Uhbj5N)LaQj!wdIe!-b004GLL_t&-)18pX z4udcZ1u-#g(~z+5JN*AY5?>Gw7hsN~k)CYt4dQDFxbs5*_&e@Hj)wtt(&JE<3Eq*D z;_gQLvqXoKv=I*gWqM9C(Tvu0>=?hTbOp9!6k6AF;>f6|S5%jGEE}TA9h)e`Yuiu8 d7)l?o1NFcJg%EAfM$P~L002ovPDHLkV1g^Dnv?(l delta 550 zcmV+>0@?ki0<;8>8Gi-<0051N9Sr~g00DDSM?wIu&K&6g00HhvL_t(I5v`QFOB_)Y z#?QI;j_a;jjf#Z$YJ7mH(xecJU?W)A`9CN~KrBV85C}GDQ=|;GDFPNjtWty!L{u=? zh>8yo%^GE+J9o~_IZFoiamQVQXP7%LzTbT3F@uf+9x&7cvVV%GdjTaC;zf>@mq<=3 z!c<%*UT)@yJ|0BK6~d4Jx-*KV`ZQ(@VyUPupum=XhInNG#Z_k-X|hK{B}~9IfiWx} zLD5QY6Vm)p0NrWymdkrHPN5Vgwd>5>4HI1=@PA+e^rq~CEj|n2X`??)0mUI*D{KBn zjv{V=y5X9|X@3grkpcXC6oou4ML~ezCc2EtnsQTB4tWNg?4bkf;hG7IMfhgNI(FV5 zGs4|*GyMTIY0$B=_*mso9+>eB z?J{?+FLkYu+4_Uk`r_>LHF~flZm0oBf#vr8%vJ>#p~!KNvqGG3)|f1T_)ydeh8$vDceZ>oNbH^|*hJ*t?Yc*1`WB&W>VYVEzu) zq#7;;VjO)t*nbgf(!`OXJBr45rP>>AQr$6c7slJWvbpNW@KTwna6d?PP>hvXCcp=4 zF;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f<+$JJpcdz delta 1274 zcmV@pi1MCNO0zH7s z{8#}P0)7Ba8DqYf&QgSne>X__O83t$NZM4&R0{XJq|x}oAU?tcfC@|eNz$04T}34& z8DJf78R&>*Zz`k$q{`#gfGHnx7nlH^G{y`jfER)1<_fNi<9aM%_zrm1C`yPkKma(+ ztQ;y*CR2bbBYz>zG*SVsfpkGU(q>uHZf3iogk_%#9E|5SWeHrmAo>P;ejX7mwq#*} zW25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+X$F_KMdb6sRz!~7K zkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&IDi_4_D!s#MVXp|-XhH;H z#&@_;oApJVd}}5O@b=X_gJboD^-fM@6|#V@sA%X)Rlkd}3MLH0dGXGG&-HX|aD~|M zC)W#H7=H?AbtdaV#dGpubj_O^J-SlWpVNv-5(;wR%mvE9`Qaqo>03b&##eNNf=m#B z9@^lsd8tJ;BvI86kNV zc~0CY(7V{s+h%cWG|y=gt|q`z$l<(@qU=i?9q#uz`G?PgDMK!VMGidHZt*N+1L0ZI zFkH=mFtywc6rJ}C_?)=m)18V!ZQ`*-j(D`gCFK|nt#{bk*%%zuQ7o7kvJgA^=(^7b zzkm5GZ;jxRn{Wup8IOUx8D4uh&(=Ox-7$a;U><*5L^!% zxRlw)vAbh;sdlR||&e}8_8%)c2Fwy=F& zH|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}Jb#viX>Oi;kBKp1x_fc0#UIbIeSJ^EkWFox zijdim{ojmn@#7EC*aY;fC0W*WN+DmQtE06pNK3SfZ^#@2K`6RgEuU_KwJTQ>E?Yar zc_9e#I$F8%>kuy-JI6ocSsYvQGbsxUCx04(w1z-pMRz9`kH5smmF@WHEG?dcYkv){ zV?kn3XB$_3zr*h1Uow)(<5)w5;3Wh1jHI)`ZlXp&!yEV{Y_~@;?CLwq;4eeaGOe6( zEsSSbwSGD0-`dUUGM-ShrilfUZt{^9lhT*&z4_x{-O{Rv#2V9EI}xb^~1iQe@7)8g(7UZ4B@ z|4zgB>+<*9=;^^)>d)H7pzGjuM>Jnezy3`@G2r z?{~a!Fj;`+8Gq^x2Jl;?IEV8)=fG217*|@)CCYgFze-x?IFODUIA>nWKpE+bn~n7; z-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGrXPIdeRE&b2Thd#{MtDK$ zpx*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{HY|nMnXd&JOovdH8X7V5?1^Y=vK~!ko-J4%*6h$1z_l{zTu}>N$Y77dN z(jrej`JjnWDIm3fj{j>}J%k>VpVM zMunJ?rSR(^OuXDgm2)PP%Lw)()f=TG1B~ScNUFa-({vjDk;dweRiFe?w-6Qho(O1_ zv!(2WV2ZhFC1SqPt}wig>|5C zrh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)yRAZ>eDe#*r`yDAVgB_R* zLB*MAc8_?!g7#WjJA zNf*S~m|;6j!A4w$ko3-C-D?f3QiNoOywjDS!K#57`tfjzaqOr$8SWAG-j-YxSgf$JEO3s=FUciZf^T1|d zdlv{cAz-VWC8|7CEV-;Wb6Vzrt)AkMWOkTe+ZBtZc)X@JVej7(9Qa3q{qv~yUkR%F zgV1zYf*?t3UMs{3OLcKP1Z6m=c&$AQlc=-2K7W6gDCe$axhg&7qBi(Mc=7aOu!`S0t-8gf#ZQK=m_VkJUaO-56fxM&#U}>8ioQPQ~9Xan>71|{&AvQNWKoV z(G*V$cD|NEzl(OC?HDr#Cqt&AdqP30PY2p48uOaogm_>#S_o_EvD7yf32g)`v6|+S zX@6g&FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zmZQj(aA_HeBY&OC^ zjj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5KhX*|AU4QE#~SgPzO zXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&f`88QO)34l90xUaIcrN!i^H~!$VzZpscObr z3PVpq)=3d6{*YiK7;ZBp6>?f?;EtO_0nMBTIICp>R=3LV88-e@FYC%|E0}pO*gziiBLfe{%Kc@qo)p8GVT7N0* z4M_Lw1tG5n(zZ5$P*4jGZTlL!ZFJhUpIRgx=rAmS%;sT8&)W?`?kC{()PbwS3u#;G z5xOo6ZIjcs{+JdGz5K@sSo14D=FzK={`?LQo~r_Pel@s?4}xpcmx|K19GZo;!D-un zE}eyzVa=&&Sk`n2mb~yf2+vl6yMJIGxIEq&SWRe)op$60@i246YB3>oE(3e2L-^}4_|K@$pmRb!NBBQzlNb;zJF zMc&w;%{On(HbQ| z@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)yI9C9*oUga6 z=hxw6QasLPnee@3^pcqGR@o#L@+8nuG5suzgA#ZC&s z|EF-4U3#nH>r^ME@~U|CYWRjZ`yN=c=Fr}#_Mgg|JQ_F~MDJ{2FSyz9PS&T@VVxu? zJm1Eneyq~b<9m$74O-iHG@!Fk->^qks+0-Tx2T+XVGXw8twMc3$0rG>+mL)4wdl~R g1N9*XHQJT-A9HGq3eLdY0ssI207*qoM6N<$f)O(SQ~&?~ diff --git a/packages/screen_state/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/screen_state/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png index 4cde12118dda48d71e01fcb589a74d069c5d7cb5..4cd7b0099ca80c806f8fe495613e8d6c69460d76 100644 GIT binary patch delta 266 zcmV+l0rmcY2$}+r8Gi!+003c4mpuRg09{Z_R7L;)|5U~JDYo_jSDX9(|7FYh`2GLd z^Zv2r{H^2sT*&w!Y^SB+`<>qVZqE6)=lqo0`vF#&*75!I`TIh@_d&k*HoEtQyV-iD z%Xz2D9EQRbeYh5Nr~y=#0ZD;^+vz0$004MNL_t(2&&|%+4u6C&2tZM$Wf&dzefR%A z(^3-?6X>hnCz2Ba@RH&`m!pgy?n@#@AuLYB&}Q)FGY`?vcft0!vht0Z@M&ZeNCWXh75gzRTXR8EE3oN&6 Q00000NkvXXt^-0~g5kS`djJ3c delta 1014 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 Zxpx7IC@PekH?;r&002ovPDHLkV1i)CYaajr delta 1916 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 delta 1274 zcmV@pi1MCNO0zH7s z{8#}P0)7Ba8DqYf&QgSne>X__O83t$NZM4&R0{XJq|x}oAU?tcfC@|eNz$04T}34& z8DJf78R&>*Zz`k$q{`#gfGHnx7nlH^G{y`jfER)1<_fNi<9aM%_zrm1C`yPkKma(+ ztQ;y*CR2bbBYz>zG*SVsfpkGU(q>uHZf3iogk_%#9E|5SWeHrmAo>P;ejX7mwq#*} zW25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+X$F_KMdb6sRz!~7K zkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&IDi_4_D!s#MVXp|-XhH;H z#&@_;oApJVd}}5O@b=X_gJboD^-fM@6|#V@sA%X)Rlkd}3MLH0dGXGG&-HX|aD~|M zC)W#H7=H?AbtdaV#dGpubj_O^J-SlWpVNv-5(;wR%mvE9`Qaqo>03b&##eNNf=m#B z9@^lsd8tJ;BvI86kNV zc~0CY(7V{s+h%cWG|y=gt|q`z$l<(@qU=i?9q#uz`G?PgDMK!VMGidHZt*N+1L0ZI zFkH=mFtywc6rJ}C_?)=m)18V!ZQ`*-j(D`gCFK|nt#{bk*%%zuQ7o7kvJgA^=(^7b zzkm5GZ;jxRn{Wup8IOUx8D4uh&(=Ox-7$a;U><*5L^!% zxRlw)vAbh;sdlR||&e}8_8%)c2Fwy=F& zH|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}Jb#viX>Oi;kBKp1x_fc0#UIbIeSJ^EkWFox zijdim{ojmn@#7EC*aY;fC0W*WN+DmQtE06pNK3SfZ^#@2K`6RgEuU_KwJTQ>E?Yar zc_9e#I$F8%>kuy-JI6ocSsYvQGbsxUCx04(w1z-pMRz9`kH5smmF@WHEG?dcYkv){ zV?kn3XB$_3zr*h1Uow)(<5)w5;3Wh1jHI)`ZlXp&!yEV{Y_~@;?CLwq;4eeaGOe6( zEsSSbwSGD0-`dUUl014$1_O8Gi!+006nq0-pc?0H{z*R7L;)|5U~JDYo_jSDXF*|5nEMy6F5^ z$M}8I`uzU?*Yf=uXr;5|{0m;6_Wb|A>ik^D_|)+I$?g3CSDK^3+eX0mD!2CP`2NN0 z{dLg!a?km&%iyTt`yiax0acdp`~T(l{$a`ZF1YpsRg(cvjDG_-U$Er-fz#Bw>2W$eUI#iU z)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G!hkE!s;%oku3;IwG3U^2k zw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn_j%}l|2+O?a>_7qq7W zmx(qtA2nV^tZlLpy_#$U%ZNx5;$`0L&dZ!@e7rFXPGAOup%q`|03hpdtXsPP0000< KMNUMnLSTZ1N;Pr- delta 1891 zcmV-p2b}oI1m_Nr8Gi-<0052=@~r>>2QEoOK~#9!?VW3E6jc<*XLh$yKNt;)Mial3 z7z%<>zxaV5DhMs*(b6YIW1=KP6Jj(m21QYbiJ}su&;o5EN=$%gptMj6p|(7#AOTUJ zlt8fsX(iGq?ZQ50=XmbU+~w|cmz~|6$KBbz$-g^IcV>Hk`+q<8%-p?uMi3G-0B~!5 ze-yPCwFPw?HGmpMc~K)7BCq;C528+>zC*o^8h^XKC)IFgkv#xzm!ewK7j|kRa9dFo zC>MoDSR@P2#cWSU{i1oH5K2-Xb3jRz>|h7VOh0K` zhq^--L3H}A0r)nr z;Tr|-kPjB1s=ItpnS`oT%|U=a4oK-ZFIE^YBLH{u2#~@%%D^K)$`9*Tg(~9M-B+Zj z;~H?4LVsEt0eFtN4&>H(DZ@KpI6RhBKLL21CxC`J&m4Gc^9wwMZU#7SR1+KtuhSZM z+yLY}Vekzw6T_ApfEkuB_yU;e&a)L@rX~z70A_N+upOXN!qygmPDmKG0d%7CECcAI zgkd>ArzH$a0XjKsO$X@IgkcH5Y;m3`0G*yNOn(KK4GF_EfL4aB5i1j9o&Z{vFk~k> z&?@K2jQcJO%W!cddG(_DyfSoO55bUMHtbDF8DPkwF^~Ql#Eq4w15k{h%ML5Ar&pzi zl-D7v8kQXQ!&RRgKCW#5DZB$$6?mjWm50rRw*ukK>P-GkA|k69h{NARc>e}uLx+U4 z0DqE>7pa}9Fez+Vc-3jb`%i^uulglFoMzAVR|2%rf= zf#;74FXF^Ku_4+G&-4$KVy%YP>%2rxu2VG_cdm?XRjEhF&wPXJ># z_Q2+jGs=l~Fyx#MmGn+PZ0`@kBfGp|fO;Vov<$;z`(+sSZ7;Y=zXaF(8rb@CuQDV^ zq3i(2LfqO%AS!Ss>V%j7%>{6mtbYQrtQK5V4InPq0NZSaXv+f2U=&2}Z6OvkBfNHi z{LSaVJ!d5dC2K*ft_L^DRk;boQhOoVw!~Kt#0b2vd%!(&DF|~u1F@nG#LA5zR&7Fv z4GKgXooMSKb1g)6Obo-rgpuEP20T;W0Aa>55KC4gtQrKkAq-Hgs@FigV1GG8+rQ=z z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRet3L_uNyQ*c zE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=kyx=~RKa4{iT zm{_>_vSCm?$Ej=i6@=m%@PE9t1zZaoM}@2|h!#1K02~31S_I<0ZV=|K0}n!RRX6Ac zXmMf*5P-dLW}WPVsCKq)-x(0*txpZ2xrv3cxJ%l=7lpoNCyG< zK92ySAcmb-3m&}s@VwXv9(0#p<>B-5$bMxT;rk;OmENa6eM4D&LVo~01soUL39?R{ zyFLt3m|v?rCK7#KNu9E9Q4KV-pEUv^{rrClE&X&9I4-e7%pu_31#zGTOfC=ab%w20R*zBP+uT#l2{a~~~0wuG%6 zco*tVxK&e>%SJj*K!2tq*_h&ES5S9@TKb8WzpK;`&b9dNdxh4S)z%Q)o`aYWUh}9L z(`p!#WO5IxI|nf?yz{90R93Ed6@2qim*}Zjj$H&Esd`?JsFJUnDfiAgF_eYiWR3GC z>M9SHDylEWrA(%mfm~;u7OU9!Wz^!7Z%jZF zi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i0WYBP d*#0Ks^FNSabJA*5${_#%002ovPDHLkV1gB0Vle;! diff --git a/packages/screen_state/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/screen_state/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png index a6d6b8609df07bf62e5100a53a01510388bd2b22..0ec303439225b78712f49115768196d8d76f6790 100644 GIT binary patch delta 850 zcmV-Y1Fih&6y64q8Gi!+000iU#^3+|0OwFlR7L;)|5U~J09TtSw)Xt~|5(QO`~Ck( z!T0|D|3<*~RmJ%E{r+;#`2ba!klFf7!uJMSo%Q?vP{jByxcAZE>;OrUCbaZYjJo^$ z{nGILmD~Da$@upC{`C6(Ey4dPw)Pyc^>5DkHoEo!QcuK-Jwl-l}t(fQKv z{dds$V#@dygS`PvhX6is7Z+@*x-d;$ zb=6f@U3Jw}_s+W3%*+b9H_vS)-R#9?zrXogeLVI2We2RFTTAL}&3C8PS~<5D&v@UI z+`s*$wqQ=yd$laNUY-|ovcS9~n_90tFUdl#qq0tEUXle|k{Op|DHpSrbxEeZ5~$>o%>OSe z^=41qvh3LlC2xXzu+-2eQoqs1^L>7ylB$bCP);(%(xYZL1 cY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f@rA97ytkO literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/screen_state/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/screen_state/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index a6d6b8609df07bf62e5100a53a01510388bd2b22..0ec303439225b78712f49115768196d8d76f6790 100644 GIT binary patch delta 850 zcmV-Y1Fih&6y64q8Gi!+000iU#^3+|0OwFlR7L;)|5U~J09TtSw)Xt~|5(QO`~Ck( z!T0|D|3<*~RmJ%E{r+;#`2ba!klFf7!uJMSo%Q?vP{jByxcAZE>;OrUCbaZYjJo^$ z{nGILmD~Da$@upC{`C6(Ey4dPw)Pyc^>5DkHoEo!QcuK-Jwl-l}t(fQKv z{dds$V#@dygS`PvhX6is7Z+@*x-d;$ zb=6f@U3Jw}_s+W3%*+b9H_vS)-R#9?zrXogeLVI2We2RFTTAL}&3C8PS~<5D&v@UI z+`s*$wqQ=yd$laNUY-|ovcS9~n_90tFUdl#qq0tEUXle|k{Op|DHpSrbxEeZ5~$>o%>OSe z^=41qvh3LlC2xXzu+-2eQoqs1^L>7ylB$bCP);(%(xYZL1 cY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f@rA97ytkO literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/packages/screen_state/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/screen_state/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index 75b2d164a5a98e212cca15ea7bf2ab5de5108680..e9f5fea27c705180eb716271f41b582e76dcbd90 100644 GIT binary patch delta 1668 zcmV-~27CGU9f}Q*8Gi!+000UT_5c6?0S-`1R7L;)|5U~JDYo_jSDRJE`2GI>`u+b> z#Q0do`1}6<{Qdq#!1wR$2T#*AweE>Ub09v4>;QIg_I^_2LtK$20(D{zn_^HL*3Rj70 z%=tLH_b#{gK7W9-03t&#zyHMQ{FK}Jd(rva=I|w|=9#+Ihp*3ip1$;$>j3}&1vg1V zK~#9!?b~^C5-}JC@Pyrv-6dSEqJqT}#j9#dJ@GzT@B8}xU&J@bBI>f6w6en+CeI)3 z^kC*U?}X%OD8$Fd$H&LV$H&LV$H&LV#|K5~mLYf|Vt-;AMv#QX1a!Ta~6|O(zp+Uvg&Aa=+vBNz0Rs{AlWy-99x<(ohfpEcFpW=7o}_1 z>s&Ou*hMLxE-GxhC`Z*r>&|vj>R7LXbI`f|486`~uft__uGhI}_Fc5H63j7aDDIx{dZl^-u)&qKP!qC^RMF(PhHK^33eOuhHu{hoSl0 zKYv6olX!V%A;_nLc2Q<$rqPnk@(F#u5rszb!OdKo$uh%0J)j}CG3VDtWHIM%xMVXV zmTF#h81iB>r55Is`L$8KI@d+*%{=Nx+FXJ98L0PjFIu;rGnnfYn1R5Qnp<{Jq0M1v zX=X&F8g4GYHsMFm8dDG!y@wy0LzrDkP5n}RZ}&a^{lJ!qV}DSMg`_~iho-+ zYhFY`V=ZZN~BQ&RAHmG&4 z!(on%X00A@4(8Rri!ZBBU(}gmP=BAPwO^0~hnWE5<&o5gK6CEuqlcu2V{xeEaUGt9 zX7jznS5T?%9I4$fnuB2<)EHiTmPxeQU>*)T8~uk^)KEOM+F)+AI>Y`eP$PIFuu==9 zE-`OPbnDbc|0)^xP^m`+=GW8BO)yJ!f5Qc}G(Wj}SEB>1?)30sXn)??nxVBC z)wA(BsB`AW54N{|qmikJR*%x0c`{LGsSfa|NK61pYH(r-UQ4_JXd!Rsz)=kL{GMc5{h13 z8)fF5CzHEDM>+FqY)$pdM}M_8rrW{O4m<%Dt1&gzy8K(_+x-vIN$cs;K#LctaW&OA zAuk_42tYgpa$&Njilse`1^L+zfE<)2YpPh<)0mJ;*IFF|TA%1xX3fZ$kxPfoYE=Ci z)BrMgp=;8Y9L43*j@*RFlXvO-jQ`tkm#McyC%N^n#@P}`4hjO2}V z1RP0E%rxTfpJbnekUwBp-VB(r604xuJ$!t8e0+R-e0+R-e0+R-^7#e&>dm?Lo++vT O0000jJBgitF5mAp-i>4+KS_oR{|13AP->1TD4=w)g|)JHOx|a2Wk1Va z!k)vP$UcQ#mdj%wNQoaJ!w>jv_6&JPyutpQps?s5dmDQ>`%?Bvj>o<%kYG!YW6H-z zu`g$@mp`;qDR!51QaS}|ZToSuAGcJ7$2HF0z`ln4t!#Yg46>;vGG9N9{V@9z#}6v* zfP?}r6b{*-C*)(S>NECI_E~{QYzN5SXRmVnP<=gzP+_Sp(Aza_hKlZ{C1D&l*(7IKXxQC1Z9#6wx}YrGcn~g%;icdw>T0Rf^w0{ z$_wn1J+C0@!jCV<%Go5LA45e{5gY9PvZp8uM$=1}XDI+9m7!A95L>q>>oe0$nC->i zeexUIvq%Uk<-$>DiDb?!In)lAmtuMWxvWlk`2>4lNuhSsjAf2*2tjT`y;@d}($o)S zn(+W&hJ1p0xy@oxP%AM15->wPLp{H!k)BdBD$toBpJh+crWdsNV)qsHaqLg2_s|Ih z`8E9z{E3sA!}5aKu?T!#enD(wLw?IT?k-yWVHZ8Akz4k5(TZJN^zZgm&zM28sfTD2BYJ|Fde3Xzh;;S` z=GXTnY4Xc)8nYoz6&vF;P7{xRF-{|2Xs5>a5)@BrnQ}I(_x7Cgpx#5&Td^4Q9_FnQ zX5so*;#8-J8#c$OlA&JyPp$LKUhC~-e~Ij!L%uSMu!-VZG7Hx-L{m2DVR2i=GR(_% zCVD!4N`I)&Q5S`?P&fQZ=4#Dgt_v2-DzkT}K(9gF0L(owe-Id$Rc2qZVLqI_M_DyO z9@LC#U28_LU{;wGZ&))}0R2P4MhajKCd^K#D+JJ&JIXZ_p#@+7J9A&P<0kdRujtQ_ zOy>3=C$kgi6$0pW06KaLz!21oOryKM3ZUOWqppndxfH}QpgjEJ`j7Tzn5bk6K&@RA?vl##y z$?V~1E(!wB5rH`>3nc&@)|#<1dN2cMzzm=PGhQ|Yppne(C-Vlt450IXc`J4R0W@I7 zd1e5uW6juvO%ni(WX7BsKx3MLngO7rHO;^R5I~0^nE^9^E_eYLgiR9&KnJ)pBbfno zSVnW$0R+&6jOOsZ82}nJ126+c|%svPo;TeUku<2G7%?$oft zyaO;tVo}(W)VsTUhq^XmFi#2z%-W9a{7mXn{uzivYQ_d6b7VJG{77naW(vHt-uhnY zVN#d!JTqVh(7r-lhtXVU6o})aZbDt_;&wJVGl2FKYFBFpU-#9U)z#(A%=IVnqytR$SY-sO( z($oNE09{D^@OuYPz&w~?9>Fl5`g9u&ecFGhqX=^#fmR=we0CJw+5xna*@oHnkahk+ z9aWeE3v|An+O5%?4fA&$Fgu~H_YmqR!yIU!bFCk4!#pAj%(lI(A5n)n@Id#M)O9Yx zJU9oKy{sRAIV3=5>(s8n{8ryJ!;ho}%pn6hZKTKbqk=&m=f*UnK$zW3YQP*)pw$O* zIfLA^!-bmBl6%d_n$#tP8Zd_(XdA*z*WH|E_yILwjtI~;jK#v-6jMl^?<%Y%`gvpwv&cFb$||^v4D&V=aNy?NGo620jL3VZnA%s zH~I|qPzB~e(;p;b^gJr7Ure#7?8%F0m4vzzPy^^(q4q1OdthF}Fi*RmVZN1OwTsAP zn9CZP`FazX3^kG(KodIZ=Kty8DLTy--UKfa1$6XugS zk%6v$Kmxt6U!YMx0JQ)0qX*{CXwZZk$vEROidEc7=J-1;peNat!vS<3P-FT5po>iE z!l3R+<`#x|+_hw!HjQGV=8!q|76y8L7N8gP3$%0kfush|u0uU^?dKBaeRSBUpOZ0c z62;D&Mdn2}N}xHRFTRI?zRv=>=AjHgH}`2k4WK=#AHB)UFrR-J87GgX*x5fL^W2#d z=(%K8-oZfMO=i{aWRDg=FX}UubM4eotRDcn;OR#{3q=*?3mE3_oJ-~prjhxh%PgQT zyn)Qozaq0@o&|LEgS{Ind4Swsr;b`u185hZPOBLL<`d2%^Yp1?oL)=jnLi;Zo0ZDliTtQ^b5SmfIMe{T==zZkbvn$KTQGlbG8w}s@M3TZnde;1Am46P3juKb zl9GU&3F=q`>j!`?SyH#r@O59%@aMX^rx}Nxe<>NqpUp5=lX1ojGDIR*-D^SDuvCKF z?3$xG(gVUsBERef_YjPFl^rU9EtD{pt z0CXwpN7BN3!8>hajGaTVk-wl=9rxmfWtIhC{mheHgStLi^+Nz12a?4r(fz)?3A%at zMlvQmL<2-R)-@G1wJ0^zQK%mR=r4d{Y3fHp){nWXUL#|CqXl(+v+qDh>FkF9`eWrW zfr^D%LNfOcTNvtx0JXR35J0~Jpi2#P3Q&80w+nqNfc}&G0A~*)lGHKv=^FE+b(37|)zL;KLF>oiGfb(?&1 zV3XRu!Sw>@quKiab%g6jun#oZ%!>V#A%+lNc?q>6+VvyAn=kf_6z^(TZUa4Eelh{{ zqFX-#dY(EV@7l$NE&kv9u9BR8&Ojd#ZGJ6l8_BW}^r?DIS_rU2(XaGOK z225E@kH5Opf+CgD^{y29jD4gHbGf{1MD6ggQ&%>UG4WyPh5q_tb`{@_34B?xfSO*| zZv8!)q;^o-bz`MuxXk*G^}(6)ACb@=Lfs`Hxoh>`Y0NE8QRQ!*p|SH@{r8=%RKd4p z+#Ty^-0kb=-H-O`nAA3_6>2z(D=~Tbs(n8LHxD0`R0_ATFqp-SdY3(bZ3;VUM?J=O zKCNsxsgt@|&nKMC=*+ZqmLHhX1KHbAJs{nGVMs6~TiF%Q)P@>!koa$%oS zjXa=!5>P`vC-a}ln!uH1ooeI&v?=?v7?1n~P(wZ~0>xWxd_Aw;+}9#eULM7M8&E?Y zC-ZLhi3RoM92SXUb-5i-Lmt5_rfjE{6y^+24`y$1lywLyHO!)Boa7438K4#iLe?rh z2O~YGSgFUBH?og*6=r9rme=peP~ah`(8Zt7V)j5!V0KPFf_mebo3z95U8(up$-+EA^9dTRLq>Yl)YMBuch9%=e5B`Vnb>o zt03=kq;k2TgGe4|lGne&zJa~h(UGutjP_zr?a7~#b)@15XNA>Dj(m=gg2Q5V4-$)D|Q9}R#002ovPDHLkV1o7DH3k3x diff --git a/packages/screen_state/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/screen_state/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index c4df70d39da7941ef3f6dcb7f06a192d8dcb308d..84ac32ae7d989f82d5e46a60405adcc8279e8001 100644 GIT binary patch delta 749 zcmVg;Ps8|O$@u8^{Z_{KM!@$5TAfS6_e#O{MZfpz`2O`0$7~@NRr(1{THzH08y3x{{PYM{eL;T_A9^tcF_4Sxb`8l z_9V3RD6;a(-0A^Pjsi!1?)d#Ap4Tk3^CP0(07;VpJ7@tgQ}z4)*zx@&yZwC9`DV-b z0ZobH_5IB4{KxD3;p_6%|f=bdFhu+F!zMZ2UFj;GUKX7tI;hv3{q~!*pMj75WP_c}> z6)IWvg5_yyg<9Op()eD1hWC19M@?_9_MHec{Z8n3FMs~w_u?Av_yNBmRxVYrpi(M% zFMP21g+hmocQp3ay*Su=qM6He)*HaaTg$E^sym`(t%s3A)x!M+vfjXUBEpK6X9%iU zU!u9jj3(-$dM~sJ%Liy#?|+!6IY#MTau#O6vVj`yh_7%Ni!?!VS+MPTO(_fG+1<#p zqu;A#i+_(N%CmVnYvb>#nA{>Q%3E`Ds7<~jZMywn@h2t>G-LrYy7?Dj{aZqhQd6tzX%(Trn+ z)HNF}%-F{rr=m*0{=a;s#YDL00000NkvXXu0mjfaGjYE delta 1884 zcmV-i2c!7<1>g>l8Gi-<0076AQ7Zrd2Pa8HK~#9!?VNjT6h$1z_m0EFf5bmb1dTDK zp;kdKV1h(V(8Sc1M<37!RE>znAk{x4#zX@eOeE1j3~!+nB5IL z<xS}u?#DBMB>w^b($1Z)`9G?eP95EKi& z$eOy@K%h;ryrR3la%;>|o*>CgB(s>dDcNOXg}CK9SPmD?Uu$P4(=PGA0ShFasNfcIHTL?9WjB9#(2xSLC z`0%$#9DW9F;B4mbU{BlaYx!SjF!QSeF~(msQRxwboh5B_O$BWOQja)GboJz$&!?mgB&3$ytsA zvns&b3Cl5Hx#%p%faR*Q906u&fbXy$maV`n?S>A)vJIH!F-vxCrY+rq5_JA(GcOgu7(Ky4X3ATR9z8*%k&<5qYeV&4Y`~}XYmK(j{)!g8d2UgHXIINM!Rvn zKtEq~Foe0s!U{kux~F6Y7Sp+2f|*Cc${S{@oh8D0=XhB8Ec-w9CflfL+te4ium2cU zoPTCj_m<3d#gjK=<*8R`HP^C$lOPM5d~UhKhRRmvv{LI za^|oavk1$QiEApSrP@~Jjbg`<*dW4TO@DPEEX$Tg$xh?Y>Qd}y@kaH~IT8!lLpS^J zR7(&wZSI6+>Eb)tX>9Z?GX#q$u z4I>7e#b7ojyJ1grOh!^}s8S#ubi^Jkd1?UK)3mp6rI^_zxRY zrx6_QmhoWoDR`fp4R7gu6@OBFGu7IDVR6~nJsB{^f5jHn<{WJ&&f^X?3f8TIk3#U& zu1*Q-e@;snJxNx8-PBnpI|uFTKN!+Lp;fPfZ+eqqU^Y1|#DJY~126?zOx-+d>%4*? z&o`TbrXSNXZW^!P0t2>@$6&aiBtUDh2wLXLD9&a(1J=k_FK|iGbAQ@x4Qmx}Ms+*; zze&q6bH(=wYuXHfz0H6<05!LkE4rl~v^!bj=^9d+vI5fN<;GP>*Pas=q2l9RxDkk` zPRk&EQI+t_0$Y%nKE)Ma)W?jaA@4Z{h zTk*7;;#lG?hvTN_On=Jaxp%bdE;mDq(q#dgdYF|-?wrMeI4h`$idZ6^VyXZVlaCd0 z;i)OYR3npf@9>00Gqn##Zb4HRurgaWFCzL9u6@J@sse>Z1XznxWvSy%Td32I3!#YN zXt9v0)RQtDDZRd?#WY?~KF7A0UcR{jt9 W+;fr}hV%pg0000&=UXv0SHh`R7L;)|5U~JDYo_jSDRDC`1<|-SjPDL z{{Q{{{{H{}09Kk-#rR9Y_viNgVafPO!S|ls`uzR=MZfp^{QU=8od8La1X`Tr_Wmff z_5e$ivgQ1@=KMy$_g9a+`TPAle6cOJ_Fc#L7qIpvwDkd1mw$fK`6IOUD75rX!}mad zv(fMTE4=(Nx%L54lL1hVF1YpqNrC`FddBPg#_Ietx%Lrkq5wX00X1L{S%Cm9QY*av z#_Rh5PKy9KYTWbvz3BX9%J>0Hi1+#X{rLA{m%$Kamk?i!03AC38#Yrxs)5QTeTVRiEmz~MKK1WAjCw(c-JK6eox;2O)?`?TG`AHia671e^vgmp!llK zp|=5sVHk#C7=~epA~VAf-~%aPC=%Qw01h8mnSZ|p?tc*y?iZ$PR7_ceEIapF3KB14K0Pog?7wtd+^xgUCa_GVmlD z<^nU>AU_Yn-JU?NFdu|wf^bTCNf-wSBYVZltDdvGBln-YrbeGvJ!|s{#`gjN@yAMb zM6cjFz0eFECCsc|_8hTa3*9-JQGehksdoVP^K4m?&wpA~+|b%{EP5D-+7h)6CE; z*{>BP=GRR3Ea}xyV*bqry{l^J=0#DaC4ej;1qs8_by?H6Tr@7hl>UKNZt)^B&yl;)&oqzLg zcfZxpE?3k%_iTOVywh%`XVN-E#COl+($9{v(pqSQcrz=)>G!!3HeNxbXGM@})1|9g zG4*@(OBaMvY0P0_TfMFPh fVHk#CZX3S=^^2mI>Ux-D00000NkvXXu0mjfzK(<8 literal 3294 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 cX)(b1KSk$MOV07*qoM6N<$f&{Qds= z{r_0T`1}6fwc-8!#-TGX}_?g)CZq4{k!uZ_g@DrQdoW0kI zu+W69&uN^)W`CK&06mMNcYMVF00dG=L_t(|+U?wHQxh>12H+Dm+1+fh+IF>G0SjJM zkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJTkdTm&kdTm&kdTm&kdP`e zsgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>VI$fQI%^ugM`#6By?GeadWcu z0gy9!D`m!H>Bd!JW(@avE8`|5XX(0PN}!8K>`dkavs;rHL+wy96QGNT=S@#7%xtlm zIW!++@*2zm-Py#Zr`DzqsLm!b{iskFNULSqE9A>SqHem>o31A%XL>S_5?=;V_i_y+ z(xxXhnt#r-l1Y8_*h`r?8Tr|)(RAiO)4jQR`13X0mx07C&p@KBP_2s``KEhv^|*8c z$$_T(v6^1Ig=#R}sE{vjA?ErGDZGUsyoJuWdJMc7Nb1^KF)-u<7q zPy$=;)0>vuWuK2hQhswLf!9yg`88u&eBbR8uhod?Nw09AXH}-#qOLLxeT2%C;R)QQ$Za#qp~cM&YVmS4i-*Fpd!cC zBXc?(4wcg>sHmXGd^VdE<5QX{Kyz$;$sCPl(_*-P2Iw?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF34$0Z;QO!J zOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUCUoZo%k(yku QW&i*H07*qoM6N<$g47z!?*IS* literal 3612 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 iW8 CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Screen State Example CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -11,7 +13,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - screen_state_example + example CFBundlePackageType APPL CFBundleShortVersionString @@ -39,7 +41,9 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - UIViewControllerBasedStatusBarAppearance - + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/packages/screen_state/example/ios/RunnerTests/RunnerTests.swift b/packages/screen_state/example/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000..86a7c3b1b --- /dev/null +++ b/packages/screen_state/example/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/packages/screen_state/example/pubspec.yaml b/packages/screen_state/example/pubspec.yaml index 4f32c97c4..4dde4fad2 100644 --- a/packages/screen_state/example/pubspec.yaml +++ b/packages/screen_state/example/pubspec.yaml @@ -1,70 +1,21 @@ name: screen_state_example description: Demonstrates how to use the screen_state plugin. - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. publish_to: "none" # Remove this line if you wish to publish to pub.dev +version: 1.0.0+1 environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.1.0 <4.0.0" dependencies: flutter: sdk: flutter - screen_state: - # When depending on this package from a real application you should use: - # screen_state: ^x.y.z - # See https://dart.dev/tools/pub/dependencies#version-constraints - # The example app is bundled with the plugin so we use a path dependency on - # the parent directory to use the current plugin's version. path: ../ - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 + cupertino_icons: ^1.0.6 dev_dependencies: flutter_test: sdk: flutter -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. flutter: - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages + uses-material-design: true \ No newline at end of file diff --git a/packages/screen_state/ios/Classes/SwiftScreenStatePlugin.swift b/packages/screen_state/ios/Classes/SwiftScreenStatePlugin.swift index 47ec31f4e..da5a57491 100644 --- a/packages/screen_state/ios/Classes/SwiftScreenStatePlugin.swift +++ b/packages/screen_state/ios/Classes/SwiftScreenStatePlugin.swift @@ -3,12 +3,89 @@ import UIKit public class SwiftScreenStatePlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "screen_state", binaryMessenger: registrar.messenger()) - let instance = SwiftScreenStatePlugin() - registrar.addMethodCallDelegate(instance, channel: channel) + let screenStateDetector = ScreenStateDetector() + let channel = FlutterEventChannel.init(name: "screenStateEvents", binaryMessenger: registrar.messenger()) + channel.setStreamHandler(screenStateDetector) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { result("iOS " + UIDevice.current.systemVersion) } } + +// Convert from string to enum +enum ScreenState: String { + case on = "SCREEN_ON" + case off = "SCREEN_OFF" + case unlock = "SCREEN_UNLOCKED" + case unknown = "UNKNOWN" + + init(fromString string: String) { + switch string { + case "SCREEN_ON": + self = .on + case "SCREEN_OFF": + self = .off + case "SCREEN_UNLOCKED": + self = .unlock + default: + self = .unknown + } + } + +} + +public class ScreenStateDetector: NSObject, FlutterStreamHandler { + private var eventSink: FlutterEventSink? + private var lastState: ScreenState = .unknown + + private func handleEvent(screenState: ScreenState) { + if (screenState == .unknown || eventSink == nil) { + return + } + + eventSink!(screenState.rawValue) + } + + @objc + private func handleStateChange() { + if UIApplication.shared.isProtectedDataAvailable { + let screenState: ScreenState = UIScreen.main.brightness > 0 ? .on : .off + + if(screenState == .on && lastState == .unlock) { + return + } + + if screenState != lastState { + lastState = screenState + handleEvent(screenState: screenState) + } + } + } + + @objc + private func handleScreenLockChanged() { + if UIApplication.shared.isProtectedDataAvailable { + if (lastState == .on) { + lastState = .unlock + handleEvent(screenState: .unlock) + } + } + } + + public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { + self.eventSink = events + + NotificationCenter.default.addObserver(self, selector: #selector(handleStateChange), name: UIScreen.brightnessDidChangeNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(handleScreenLockChanged), name: UIApplication.protectedDataDidBecomeAvailableNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(handleScreenLockChanged), name: UIApplication.protectedDataWillBecomeUnavailableNotification, object: nil) + + return nil + } + + public func onCancel(withArguments arguments: Any?) -> FlutterError? { + NotificationCenter.default.removeObserver(self) + eventSink = nil + return nil + } +} \ No newline at end of file diff --git a/packages/screen_state/ios/screen_state.podspec b/packages/screen_state/ios/screen_state.podspec index 52455a4df..fcf5486aa 100644 --- a/packages/screen_state/ios/screen_state.podspec +++ b/packages/screen_state/ios/screen_state.podspec @@ -4,14 +4,14 @@ # Pod::Spec.new do |s| s.name = 'screen_state' - s.version = '0.0.1' - s.summary = 'A new Flutter project.' + s.version = '1.0.0' + s.summary = 'Base plugin for screen state detection.' s.description = <<-DESC -A new Flutter project. +https://github.com/cph-cachet/flutter-plugins/tree/master/packages/screen_state/ios. DESC - s.homepage = 'http://example.com' + s.homepage = 'https://github.com/cph-cachet/flutter-plugins/tree/master/packages/screen_state/ios' s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } + s.author = { 'Tokenlab' => 'luansilva@tokenlab.com.br' } s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' diff --git a/packages/screen_state/lib/screen_state.dart b/packages/screen_state/lib/screen_state.dart index 920caa70d..8fb2a4bc1 100644 --- a/packages/screen_state/lib/screen_state.dart +++ b/packages/screen_state/lib/screen_state.dart @@ -2,11 +2,51 @@ import 'dart:async'; import 'dart:io' show Platform; import 'package:flutter/services.dart'; -/// The type of screen state events coming from Android. -enum ScreenStateEvent { SCREEN_UNLOCKED, SCREEN_ON, SCREEN_OFF } +/// The type of screen state events coming from Android or iOS. +enum ScreenStateEvent { + SCREEN_UNLOCKED, + SCREEN_ON, + SCREEN_OFF; + + /// Returns the name of the enum value. + String get name { + switch (this) { + case ScreenStateEvent.SCREEN_UNLOCKED: + return Platform.isAndroid + ? 'android.intent.action.USER_PRESENT' + : 'SCREEN_UNLOCKED'; + case ScreenStateEvent.SCREEN_ON: + return Platform.isAndroid + ? 'android.intent.action.SCREEN_ON' + : 'SCREEN_ON'; + case ScreenStateEvent.SCREEN_OFF: + return Platform.isAndroid + ? 'android.intent.action.SCREEN_OFF' + : 'SCREEN_OFF'; + default: + throw new ArgumentError('Unknown ScreenStateEvent: $this'); + } + } + + /// Returns the enum value from the name. + static ScreenStateEvent fromName(String name) { + switch (name) { + case 'SCREEN_UNLOCKED': + case 'android.intent.action.USER_PRESENT': // Android only 'USER_PRESENT + return ScreenStateEvent.SCREEN_UNLOCKED; + case 'SCREEN_ON': + case 'android.intent.action.SCREEN_ON': // Android only 'SCREEN_ON' + return ScreenStateEvent.SCREEN_ON; + case 'SCREEN_OFF': + case 'android.intent.action.SCREEN_OFF': // Android only 'SCREEN_OFF' + return ScreenStateEvent.SCREEN_OFF; + default: + throw new ArgumentError('Unknown ScreenStateEvent: $name'); + } + } +} /// Custom Exception for the `screen_state` plugin, used whenever the plugin -/// is used on platforms other than Android class ScreenStateException implements Exception { String _cause; @@ -21,6 +61,7 @@ class Screen { static Screen? _singleton; EventChannel _eventChannel = const EventChannel('screenStateEvents'); Stream? _screenStateStream; + ScreenStateEvent? _lastScreenState; /// Constructs a singleton instance of [Screen]. /// @@ -31,29 +72,21 @@ class Screen { /// Stream of [ScreenStateEvent]s. /// Each event is streamed as it occurs on the phone. - /// Only Android [ScreenStateEvent] are streamed. - Stream? get screenStateStream { - if (Platform.isAndroid) { - if (_screenStateStream == null) { - _screenStateStream = _eventChannel - .receiveBroadcastStream() - .map((event) => _parseScreenStateEvent(event)); - } - return _screenStateStream; + Stream get screenStateStream { + if (!Platform.isAndroid && !Platform.isIOS) { + throw ScreenStateException( + 'Screen State API only available on Android and iOS.', + ); } - throw ScreenStateException('Screen State API only available on Android'); - } - ScreenStateEvent _parseScreenStateEvent(String event) { - switch (event) { - case 'android.intent.action.SCREEN_OFF': - return ScreenStateEvent.SCREEN_OFF; - case 'android.intent.action.SCREEN_ON': - return ScreenStateEvent.SCREEN_ON; - case 'android.intent.action.USER_PRESENT': - return ScreenStateEvent.SCREEN_UNLOCKED; - default: - throw new ArgumentError('$event was not recognized.'); + if (_screenStateStream == null) { + _screenStateStream = _eventChannel.receiveBroadcastStream().map( + (event) => ScreenStateEvent.fromName( + event, + ), + ); } + + return _screenStateStream!; } } diff --git a/packages/screen_state/pubspec.yaml b/packages/screen_state/pubspec.yaml index 0bd472dfd..fd0e9ebd4 100644 --- a/packages/screen_state/pubspec.yaml +++ b/packages/screen_state/pubspec.yaml @@ -1,6 +1,6 @@ name: screen_state -description: A plugin for reporting screen events while the flutter application is running in background. Works for Android only. -version: 3.0.1 +description: A plugin for reporting screen events while the flutter application is running in background. Works for Android and iOS only. +version: 4.0.0 homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages/screen_state environment: From 7060055b4d3680213eacd84df9c7e9dc58f2357e Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Wed, 31 Jul 2024 11:38:27 +0200 Subject: [PATCH 14/80] [screen_state] Update changelog and README for release --- packages/screen_state/CHANGELOG.md | 2 +- packages/screen_state/README.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/screen_state/CHANGELOG.md b/packages/screen_state/CHANGELOG.md index cac4d1f1a..4721e8f0e 100644 --- a/packages/screen_state/CHANGELOG.md +++ b/packages/screen_state/CHANGELOG.md @@ -1,4 +1,4 @@ -## 3.1.0 +## 4.0.0 - Added support for iOS. - Restructuring of the interaction between the plugin and native applications. diff --git a/packages/screen_state/README.md b/packages/screen_state/README.md index 7bd024baa..03b02bf87 100644 --- a/packages/screen_state/README.md +++ b/packages/screen_state/README.md @@ -42,4 +42,5 @@ The stream can also be cancelled again by calling the `cancel()` method: #### iOS: - This package will exclusively detect screen unlocks on iOS devices that are protected with a passcode. If the device lacks a passcode, it will solely detect the screen's on/off state; -- The detection doesn't function when the app is running in the iOS simulator. The plugin will only return a 'Screen_On' event. \ No newline at end of file +- The detection doesn't function when the app is running in the iOS simulator. The plugin will only return a 'Screen_On' event. +- When the user sets their brightness to the lowest setting, it is possible that this generates a `SCREEN_ON` event, a limitation to the way that this plugin detects when the screen is off. From 0a0622e3f0f8ab6dab6882ec612b019690b74150 Mon Sep 17 00:00:00 2001 From: bardram Date: Wed, 31 Jul 2024 13:08:13 +0200 Subject: [PATCH 15/80] merge of PR #971 + fix of a lot of linter + docs issues --- packages/mobility_features/CHANGELOG.md | 6 + packages/mobility_features/README.md | 94 ++++---- .../mobility_features/analysis_options.yaml | 19 ++ .../mobility_features/example/lib/main.dart | 50 ++--- .../example/lib/moves_page.dart | 34 ++- .../example/lib/places_page.dart | 16 +- .../example/lib/stops_page.dart | 16 +- .../mobility_features/example/pubspec.yaml | 10 +- .../lib/mobility_features.dart | 14 +- .../{mobility_context.dart => context.dart} | 52 ++--- .../src/{mobility_domain.dart => domain.dart} | 204 +++++++++--------- .../{mobility_features.dart => features.dart} | 64 +++--- ...mobility_file_util.dart => file_util.dart} | 4 +- ...mobility_functions.dart => functions.dart} | 50 ++--- ...ty_intermediate.dart => intermediate.dart} | 17 +- .../lib/src/mobility_serializer.dart | 65 ------ .../mobility_features/lib/src/serializer.dart | 55 +++++ packages/mobility_features/pubspec.yaml | 6 +- .../test/mobility_features_test.dart | 189 ++++++++-------- .../mobility_features/test/test_utils.dart | 30 +-- .../test/testdata/location_samples.json | 20 ++ .../example/.flutter-plugins-dependencies | 2 +- .../ios/Flutter/AppFrameworkInfo.plist | 2 +- .../example/ios/Flutter/Flutter.podspec | 12 +- .../ios/Flutter/flutter_export_environment.sh | 5 +- packages/pedometer/example/ios/Podfile | 2 +- .../ios/Runner.xcodeproj/project.pbxproj | 25 ++- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../pedometer/example/ios/Runner/Info.plist | 4 + 29 files changed, 537 insertions(+), 532 deletions(-) create mode 100644 packages/mobility_features/analysis_options.yaml rename packages/mobility_features/lib/src/{mobility_context.dart => context.dart} (80%) rename packages/mobility_features/lib/src/{mobility_domain.dart => domain.dart} (67%) rename packages/mobility_features/lib/src/{mobility_features.dart => features.dart} (77%) rename packages/mobility_features/lib/src/{mobility_file_util.dart => file_util.dart} (92%) rename packages/mobility_features/lib/src/{mobility_functions.dart => functions.dart} (65%) rename packages/mobility_features/lib/src/{mobility_intermediate.dart => intermediate.dart} (89%) delete mode 100644 packages/mobility_features/lib/src/mobility_serializer.dart create mode 100644 packages/mobility_features/lib/src/serializer.dart diff --git a/packages/mobility_features/CHANGELOG.md b/packages/mobility_features/CHANGELOG.md index 7555c34ad..5baba2452 100644 --- a/packages/mobility_features/CHANGELOG.md +++ b/packages/mobility_features/CHANGELOG.md @@ -1,3 +1,9 @@ +## 5.0.0 + +- upgrade to Dart 3.2 and Flutter 3.16 +- improvements to API docs +- improvements to example apps (permissions - PR [#971](https://github.com/cph-cachet/flutter-plugins/pull/971)). + ## 4.0.1 - Fixed formatting diff --git a/packages/mobility_features/README.md b/packages/mobility_features/README.md index e7cfeae9f..85d5be2a6 100644 --- a/packages/mobility_features/README.md +++ b/packages/mobility_features/README.md @@ -1,11 +1,27 @@ # Mobility Features +This plugin supports the realtime calculation of mobility features based on location tracking of a phone. +The following location features are collected: + +* places +* stops +* moves + +From this, a set of derived features are calculated: + +* number of significant places +* home sStay +* location entropy +* normalized location entropy +* distance traveled + +Read more on the [theoretical background](#theoretical-background) on these mobility features below. + ## Setup -The Mobility Features package is designed to work independent of the location plugin. You may choose you own location plugin, since you may already use this in your app. +The Mobility Features package is designed to work independent of the location plugin. You may choose you own location plugin, since you may already use this in your app. -In the example app we use our own plugin [`carp_background_location`](https://pub.dev/packages/carp_background_location) which works on both Android and iOS as of August 2020. However, the -[location](https://pub.dev/packages/location) plugin will also work. The important thing, however, is to make sure that the app runs in the backgound. On Android this is tied to running the app as a foregound service. +In the example app we use our own plugin [`carp_background_location`](https://pub.dev/packages/carp_background_location) which works on both Android and iOS as of August 2020. However, the [location](https://pub.dev/packages/location) plugin will also work. The important thing, however, is to make sure that the app runs in the background. On Android this is tied to running the app as a foreground service. Add the package to your `pubspec.yaml` file and import the package @@ -15,7 +31,6 @@ import 'package:mobility_features/mobility_features.dart'; The plugin works as a singleton and can be accessed using `MobilityFeatures()` in the code. - ### Step 1 - Configuration of parameters The following configurations can be made, which will influence the algorithms for producing features: @@ -38,18 +53,17 @@ void initState() { } ``` -Features computation is triggered when the user moves around and change their geo-position by a certain distance (stop distance). +Features computation is triggered when the user moves around and change their geo-position by a certain distance (stop distance). If the stop was long enough (stop duration) the stop will be saved. Places are computed by grouping stops based on distance between them (place radius) -Common for these parameters is that their value depend on what you are trying to capture. +Common for these parameters is that their value depend on what you are trying to capture. Low parameter values will make the features more fine-grained but will trigger computation more often and will likely also lead to noisy features. For example, given a low stop duration, stopping for a red light in traffic will count as a stop. Such granularity will be irrelevant for many use cases, but may be useful if questions such as "Do a user take the same route to work every day?" - ### Step 2 - Set up location streaming -Collection of location data is not directly supported by this package, for this you have to use a location plugin such as [`carp_background_location`](https://pub.dev/packages/carp_background_location). You can to convert from whichever location object is used by the location plugin to a `LocationSample` object. -Next, you can start listening to location updates and subscribe to the `MobilityFeatures()`'s `contextStream` to be be notified each time a new set of features has been computed. +Collection of location data is not directly supported by this package, for this you have to use a location plugin such as [`carp_background_location`](https://pub.dev/packages/carp_background_location). You can to convert from whichever location object is used by the location plugin to a `LocationSample` object. +Next, you can start listening to location updates and subscribe to the `MobilityFeatures()`'s `contextStream` to be be notified each time a new set of features has been computed. Below is shown an example using the [`carp_background_location`](https://pub.dev/packages/carp_background_location) plugin, where a `LocationDto` stream is converted into a `LocationSample` stream by using a map-function. @@ -87,9 +101,11 @@ Below is shown an example using the [`carp_background_location`](https://pub.dev } ``` -### Step 3 - Handle mobility +> **Note** that access to location data needs permissions from the OS. This is **not** handled by the plugin but should be handled on an app-level. See the example app for this. Note also, that permissions for access location "ALWAYS" needs to be granted by the user in order to collect location information in the background. + +### Step 3 - Listen to mobility features -A call-back method is used to handle incoming MobilityContext objects: +The call-back method `onMobilityContext` is used to process the stream of `MobilityContext` objects: ```dart /// Handle incoming contexts @@ -99,7 +115,7 @@ void onMobilityContext(MobilityContext context) { } ``` -All features are implemented as getters for a `MobilityContext` object. +All the mobility features are accessible in the `MobilityContext` object: ```dart /// Location features @@ -112,49 +128,47 @@ context.numberOfSignificantPlaces; context.homeStay; context.entropy; context.normalizedEntropy; -context.distanceTravelled; +context.distanceTraveled; ``` -## Example -The example application included in the package shows the feature values, including separate pages for stops, moves and places. - -![](https://raw.githubusercontent.com/cph-cachet/flutter-plugins/master/packages/mobility_features/images/features.jpeg) -![](https://raw.githubusercontent.com/cph-cachet/flutter-plugins/master/packages/mobility_features/images/stops.jpeg) -![](https://raw.githubusercontent.com/cph-cachet/flutter-plugins/master/packages/mobility_features/images/places.jpeg) -![](https://raw.githubusercontent.com/cph-cachet/flutter-plugins/master/packages/mobility_features/images/moves.jpeg) - - ## Feature errors -When a feature cannot be evaluated, it will result in a value of -1.0. +When a feature cannot be calculated, it will result a value of `-1.0`. -Examples: +For example: -* The Home Stay feature requires at least *some* data to be collected between 00:00 and 06:00 otherwise the feature cannot be evaluated. +* The Home Stay feature requires at least *some* data to be collected between 00:00 and 06:00 otherwise the feature cannot be evaluated. * The Entropy and Normalized Entropy features require at least 2 places to be evaluated. If only a single place was found, this will result in an Entropy of 0. -## Theorical Background +## Example + +The example application included in the package shows the feature values, including separate pages for stops, moves and places. +It also illustrates how to ask the user for permissions to access location data, also when the app is in the background. + +![mobility_app_1](https://raw.githubusercontent.com/cph-cachet/flutter-plugins/master/packages/mobility_features/images/features.jpeg) +![mobility_app_2](https://raw.githubusercontent.com/cph-cachet/flutter-plugins/master/packages/mobility_features/images/stops.jpeg) +![mobility_app_3](https://raw.githubusercontent.com/cph-cachet/flutter-plugins/master/packages/mobility_features/images/places.jpeg) +![mobility_app_4](https://raw.githubusercontent.com/cph-cachet/flutter-plugins/master/packages/mobility_features/images/moves.jpeg) -For mental health research, location data, together with a time component, -both collected from the user’s smartphone, can be reduced to certain behavioral -features pertaining to the user’s mobility. -These features can be used to diagnose patients suffering from mental disorders such as depression. +## Theoretical Background ### Location Features -The mobility features which will be used are derived from GPS location data are: -* **Stop:** A collection of GPS points which together represent a visit at a known `Place` (see below) for an extended period of time. A `Stop` is defined by a location that represents the centroid of a collection of data points, from which a is created. In addition a `Stop` also has an `arrival` and a `departure` time-stamp, representing when the user arrived at the place and when the user left the place. From the arrival- and departure timestamps of the **Stop** the duration can be computed. +The mobility features are derived from GPS location data, like this: + +* **Stop:** A collection of GPS points which together represent a visit at a known `Place` (see below) for an extended period of time. A `Stop` is defined by a location that represents the centroid of a collection of data points, from which a is created. In addition a `Stop` also has an `arrival` and a `departure` time-stamp, representing when the user arrived at the place and when the user left the place. From the arrival- and departure timestamps of the `Stop` the duration can be computed. -* **Place:** A group of stops that were clustered by the DBSCAN algorithm. From the cluster of stops, the centroid of the stops can be found, i.e. the center location. In addition, it can be computed how long a user has visited a given place by summing over the duration of all the stops at that place. +* **Place:** A `Place` is a group of stops that were clustered by the DBSCAN algorithm. From the cluster of stops, the centroid of the stops can be found, i.e. the center location. In addition, it can be computed how long a user has visited a given place by summing over the duration of all the stops at that place. -* **Move:** The travel between two Stops, which the user will pass though a path of GPS points. The distance of a Move can be computed as the sum of using the haversine distance of this path. Given the distance travelled as well as departure and arrival timestamp from the Stops, the average speed at which the user traveled can be derived. +* **Move:** A `Move` is the travel between two stops represented as a path of GPS points. The distance of a `Move` can be computed as the sum of using the haversine distance of this path. Given the distance traveled as well as departure and arrival timestamp from the stops, the average speed at which the user traveled can be derived. ### Derived Features -* **Home Stay:** -The portion (percentage) of the total time elapsed since midnight which was spent at home. Elapsed time is calculated from the departure time of the last known stop. +A set of features can be derived from the location features: + +* **Home Stay:** The portion (percentage) of the total time elapsed since midnight which was spent at home. Elapsed time is calculated from the departure time of the last known stop. -* **Location Variance:** The statistical variance in the latitude- and longitudinal coordinates. +* **Location Variance:** The statistical variance in the latitude and longitudinal coordinates. * **Number of Places:** The number of places visited today. @@ -162,8 +176,4 @@ The portion (percentage) of the total time elapsed since midnight which was spen * **Normalized Entropy:** The normalized entropy with respect to time spent at places. -* **Distance Travelled:** The total distance travelled today (in meters), i.e. not limited to walking or running. - --------------- - -Author: Thomas Nilsson (tnni@dtu.dk) +* **Distance Traveled:** The total distance traveled today (in meters), i.e. not limited to walking or running. diff --git a/packages/mobility_features/analysis_options.yaml b/packages/mobility_features/analysis_options.yaml new file mode 100644 index 000000000..07e99cb85 --- /dev/null +++ b/packages/mobility_features/analysis_options.yaml @@ -0,0 +1,19 @@ +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options + +include: package:flutter_lints/flutter.yaml + +analyzer: + exclude: [build/**] + language: + strict-casts: true + strict-inference: true + strict-raw-types: true + +linter: + rules: + cancel_subscriptions: true + constant_identifier_names: false + depend_on_referenced_packages: true + avoid_print: false + use_string_in_part_of_directives: true diff --git a/packages/mobility_features/example/lib/main.dart b/packages/mobility_features/example/lib/main.dart index 1a697d748..61d9944d3 100644 --- a/packages/mobility_features/example/lib/main.dart +++ b/packages/mobility_features/example/lib/main.dart @@ -42,7 +42,7 @@ final moveIcon = Icon(Icons.directions_walk); final placeIcon = Icon(Icons.place); final featuresIcon = Icon(Icons.assessment); final homeStayIcon = Icon(Icons.home); -final distanceTravelledIcon = Icon(Icons.card_travel); +final distanceTraveledIcon = Icon(Icons.card_travel); final entropyIcon = Icon(Icons.equalizer); final varianceIcon = Icon(Icons.swap_calls); @@ -105,13 +105,13 @@ class _MyHomePageState extends State { /// Set up streams: /// * Location streaming to MobilityContext /// * Subscribe to MobilityContext updates - void streamInit() async { - await _requestNotificationPermission(); - await _requestManageExternalStoragePermission(); + Future streamInit() async { + await requestNotificationPermission(); + await requestManageExternalStoragePermission(); // ask for location permissions, if not already granted if (!await isLocationAlwaysGranted()) { - await _requestLocationPermission(); + await requestLocationPermission(); await askForLocationAlwaysPermission(); } @@ -166,7 +166,7 @@ class _MyHomePageState extends State { return granted; } - Future _requestLocationPermission() async { + Future requestLocationPermission() async { final result = await Permission.location.request(); if (result == PermissionStatus.granted) { @@ -176,7 +176,7 @@ class _MyHomePageState extends State { } } - Future _requestNotificationPermission() async { + Future requestNotificationPermission() async { final result = await Permission.notification.request(); if (result == PermissionStatus.granted) { @@ -186,7 +186,7 @@ class _MyHomePageState extends State { } } - Future _requestManageExternalStoragePermission() async { + Future requestManageExternalStoragePermission() async { final result = await Permission.manageExternalStorage.request(); if (result == PermissionStatus.granted) { @@ -231,9 +231,9 @@ class _MyHomePageState extends State { : "${(_mobilityContext.homeStay! * 100).toStringAsFixed(1)}%", homeStayIcon), entry( - "Distance Travelled", - "${(_mobilityContext.distanceTravelled! / 1000).toStringAsFixed(2)} km", - distanceTravelledIcon), + "Distance Traveled", + "${(_mobilityContext.distanceTraveled! / 1000).toStringAsFixed(2)} km", + distanceTraveledIcon), entry( "Normalized Entropy", "${_mobilityContext.normalizedEntropy?.toStringAsFixed(2)}", @@ -290,19 +290,17 @@ class _MyHomePageState extends State { }); } - Widget _navBar() { - return BottomNavigationBar( - onTap: onTabTapped, // new - currentIndex: _currentIndex, // this will be set when a new tab is tapped - type: BottomNavigationBarType.fixed, - items: [ - BottomNavigationBarItem(icon: featuresIcon, label: 'Features'), - BottomNavigationBarItem(icon: stopIcon, label: 'Stops'), - BottomNavigationBarItem(icon: placeIcon, label: 'Places'), - BottomNavigationBarItem(icon: moveIcon, label: 'Moves') - ], - ); - } + Widget get navBar => BottomNavigationBar( + onTap: onTabTapped, + currentIndex: _currentIndex, + type: BottomNavigationBarType.fixed, + items: [ + BottomNavigationBarItem(icon: featuresIcon, label: 'Features'), + BottomNavigationBarItem(icon: stopIcon, label: 'Stops'), + BottomNavigationBarItem(icon: placeIcon, label: 'Places'), + BottomNavigationBarItem(icon: moveIcon, label: 'Moves') + ], + ); @override Widget build(BuildContext context) { @@ -331,12 +329,10 @@ class _MyHomePageState extends State { return Scaffold( appBar: AppBar( backgroundColor: Colors.teal, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. title: Text(widget.title), ), body: pages[_currentIndex], - bottomNavigationBar: _navBar(), + bottomNavigationBar: navBar, ); } } diff --git a/packages/mobility_features/example/lib/moves_page.dart b/packages/mobility_features/example/lib/moves_page.dart index 9819672f4..01524cc56 100644 --- a/packages/mobility_features/example/lib/moves_page.dart +++ b/packages/mobility_features/example/lib/moves_page.dart @@ -1,31 +1,23 @@ -part of mobility_app; +part of 'main.dart'; class MovesPage extends StatelessWidget { final List moves; MovesPage(this.moves); - Widget moveEntry(Move m) { - return Container( - padding: const EdgeInsets.all(2), - margin: EdgeInsets.all(3), - child: ListTile( - leading: Text('Place ${m.stopFrom.placeId} → ${m.stopTo.placeId}'), - title: Text('${m.distance?.toInt()} meters'), - trailing: Text('${formatDuration(m.duration)}'), - )); - } + Widget moveEntry(Move m) => Container( + padding: const EdgeInsets.all(2), + margin: EdgeInsets.all(3), + child: ListTile( + leading: Text('Place ${m.stopFrom.placeId} → ${m.stopTo.placeId}'), + title: Text('${m.distance?.toInt()} meters'), + trailing: Text('${formatDuration(m.duration)}'), + )); - Widget list() { - return ListView.builder( - itemCount: moves.length, - itemBuilder: (ctx, index) => moveEntry(moves[index])); - } + Widget list() => ListView.builder( + itemCount: moves.length, + itemBuilder: (ctx, index) => moveEntry(moves[index])); @override - Widget build(BuildContext context) { - return Container( - child: list(), - ); - } + Widget build(BuildContext context) => Container(child: list()); } diff --git a/packages/mobility_features/example/lib/places_page.dart b/packages/mobility_features/example/lib/places_page.dart index e1d8f551b..27b4dacdf 100644 --- a/packages/mobility_features/example/lib/places_page.dart +++ b/packages/mobility_features/example/lib/places_page.dart @@ -1,4 +1,4 @@ -part of mobility_app; +part of 'main.dart'; class PlacesPage extends StatelessWidget { final List places; @@ -19,16 +19,10 @@ class PlacesPage extends StatelessWidget { )); } - Widget list() { - return ListView.builder( - itemCount: places.length, - itemBuilder: (ctx, index) => placeEntry(places[index])); - } + Widget list() => ListView.builder( + itemCount: places.length, + itemBuilder: (ctx, index) => placeEntry(places[index])); @override - Widget build(BuildContext context) { - return Container( - child: list(), - ); - } + Widget build(BuildContext context) => Container(child: list()); } diff --git a/packages/mobility_features/example/lib/stops_page.dart b/packages/mobility_features/example/lib/stops_page.dart index 66e6bb4fd..791a83822 100644 --- a/packages/mobility_features/example/lib/stops_page.dart +++ b/packages/mobility_features/example/lib/stops_page.dart @@ -1,4 +1,4 @@ -part of mobility_app; +part of 'main.dart'; class StopsPage extends StatelessWidget { final List stops; @@ -18,16 +18,10 @@ class StopsPage extends StatelessWidget { )); } - Widget list() { - return ListView.builder( - itemCount: stops.length, - itemBuilder: (ctx, index) => stopEntry(stops[index])); - } + Widget list() => ListView.builder( + itemCount: stops.length, + itemBuilder: (ctx, index) => stopEntry(stops[index])); @override - Widget build(BuildContext context) { - return Container( - child: list(), - ); - } + Widget build(BuildContext context) => Container(child: list()); } diff --git a/packages/mobility_features/example/pubspec.yaml b/packages/mobility_features/example/pubspec.yaml index 25976b229..784df1cb8 100644 --- a/packages/mobility_features/example/pubspec.yaml +++ b/packages/mobility_features/example/pubspec.yaml @@ -1,22 +1,24 @@ name: mobility_features_example description: An example app for the Mobility Features package. -publish_to: "none" # Remove this line if you wish to publish to pub.dev -version: 3.1.0 +publish_to: "none" +version: 5.0.0 environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" dependencies: flutter: sdk: flutter + permission_handler: ^11.3.1 + mobility_features: path: ../ carp_background_location: ^4.0.0 # path: ../../carp_background_location - cupertino_icons: ^1.0.5 dev_dependencies: flutter_test: diff --git a/packages/mobility_features/lib/mobility_features.dart b/packages/mobility_features/lib/mobility_features.dart index 7dc0eea0c..ee0125bd9 100644 --- a/packages/mobility_features/lib/mobility_features.dart +++ b/packages/mobility_features/lib/mobility_features.dart @@ -9,10 +9,10 @@ import 'dart:convert'; import 'dart:io'; import 'package:path_provider/path_provider.dart'; -part 'package:mobility_features/src/mobility_functions.dart'; -part 'package:mobility_features/src/mobility_domain.dart'; -part 'package:mobility_features/src/mobility_intermediate.dart'; -part 'package:mobility_features/src/mobility_context.dart'; -part 'package:mobility_features/src/mobility_serializer.dart'; -part 'package:mobility_features/src/mobility_features.dart'; -part 'package:mobility_features/src/mobility_file_util.dart'; +part 'src/context.dart'; +part 'src/domain.dart'; +part 'src/features.dart'; +part 'src/file_util.dart'; +part 'src/functions.dart'; +part 'src/intermediate.dart'; +part 'src/serializer.dart'; diff --git a/packages/mobility_features/lib/src/mobility_context.dart b/packages/mobility_features/lib/src/context.dart similarity index 80% rename from packages/mobility_features/lib/src/mobility_context.dart rename to packages/mobility_features/lib/src/context.dart index 5cef28c89..9a8b1588c 100644 --- a/packages/mobility_features/lib/src/mobility_context.dart +++ b/packages/mobility_features/lib/src/context.dart @@ -1,46 +1,40 @@ -part of mobility_features; +part of '../mobility_features.dart'; /// Daily mobility context. /// /// All Stops and Moves are on the same [date]. /// [places] are all places for which the duration on the given date is greater than 0. class MobilityContext { - late DateTime _timestamp, _date; - List _stops; - List _places; - List _moves; + late final DateTime _timestamp, _date; + final List _stops; + final List _places; + final List _moves; late List _significantPlaces; Place? _homePlace; - late _HourMatrix _hourMatrix; - double? _locationVariance, _entropy, _normalizedEntropy, _homeStay, - _distanceTravelled; - List? contexts; + _distanceTraveled; - /// Private constructor, cannot be instantiated from outside + /// Private constructor. MobilityContext._(this._stops, this._places, this._moves, this._date) { _timestamp = DateTime.now(); - // if contexts array is null, init to empty array - contexts = contexts ?? []; - // compute all the features _significantPlaces = - _places.where((p) => p.duration > Duration(minutes: 3)).toList(); + _places.where((p) => p.duration > const Duration(minutes: 3)).toList(); _hourMatrix = _HourMatrix.fromStops(_stops, _places.length); _homePlace = _findHomePlaceToday(); _homeStay = _calculateHomeStay(); _locationVariance = _calculateLocationVariance(); _entropy = _calculateEntropy(); _normalizedEntropy = _calculateNormalizedEntropy(); - _distanceTravelled = _calculateDistanceTravelled(); + _distanceTraveled = _calculateDistanceTraveled(); } - // The date of this context. + /// The date of this context. DateTime get date => _date; /// Timestamp at which the features were computed @@ -81,15 +75,14 @@ class MobilityContext { /// Normalized location entropy. A scalar between 0 and 1. double? get normalizedEntropy => _normalizedEntropy; - /// Distance travelled today in meters. - double? get distanceTravelled => _distanceTravelled; + /// Distance traveled today in meters. + double? get distanceTraveled => _distanceTraveled; - /// Private home stay calculation double? _calculateHomeStay() { if (stops.isEmpty) return null; // Latest known sample time - DateTime latestTime = _stops.last.departure; + final latestTime = _stops.last.departure; // Total time elapsed from midnight until the last stop int totalTime = latestTime.millisecondsSinceEpoch - @@ -108,9 +101,8 @@ class MobilityContext { Place? _findHomePlaceToday() => (_hourMatrix.homePlaceId == -1) ? null - : _places.where((p) => p.id == _hourMatrix.homePlaceId).first; + : _places.where((p) => p._id == _hourMatrix.homePlaceId).first; - /// Location variance calculation double? _calculateLocationVariance() { // Require at least 2 observations if (_stops.length < 2) return 0.0; @@ -125,16 +117,18 @@ class MobilityContext { double? _calculateEntropy() { // if no places were visited return null - if (places.isEmpty) + // else - the Entropy is zero when one outcome is certain to occur + if (places.isEmpty) { return null; - // the Entropy is zero when one outcome is certain to occur - else if (places.length == 1) return 0.0; + } else if (places.length == 1) { + return 0.0; + } // calculate time spent at different places List durations = places.map((p) => p.durationForDate(date)).toList(); - Duration totalTimeSpent = durations.fold(Duration(), (a, b) => a + b); + Duration totalTimeSpent = durations.fold(const Duration(), (a, b) => a + b); List distribution = durations .map((d) => (d.inMilliseconds.toDouble() / @@ -144,12 +138,10 @@ class MobilityContext { return -distribution.map((p) => p * log(p)).reduce((a, b) => (a + b)); } - /// Private normalized entropy calculation double _calculateNormalizedEntropy() => (places.length == 1) ? 0.0 : entropy! / log(places.length); - /// Private distance travelled calculation - double _calculateDistanceTravelled() => + double _calculateDistanceTraveled() => _moves.map((m) => (m.distance)).fold(0.0, (a, b) => a + b!); Map toJson() => { @@ -160,7 +152,7 @@ class MobilityContext { "num_of_significant_places": significantPlaces.length, "normalized_entropy": normalizedEntropy, "home_stay": homeStay, - "distance_travelled": distanceTravelled, + "distance_traveled": distanceTraveled, "location_variance": locationVariance }; } diff --git a/packages/mobility_features/lib/src/mobility_domain.dart b/packages/mobility_features/lib/src/domain.dart similarity index 67% rename from packages/mobility_features/lib/src/mobility_domain.dart rename to packages/mobility_features/lib/src/domain.dart index b48fa1b5d..4f3071d6e 100644 --- a/packages/mobility_features/lib/src/mobility_domain.dart +++ b/packages/mobility_features/lib/src/domain.dart @@ -1,9 +1,9 @@ -part of mobility_features; +part of '../mobility_features.dart'; const int HOURS_IN_A_DAY = 24; const String _LATITUDE = 'latitude', _LONGITUDE = 'longitude', - _DATETIME = 'datetime', + _DATE_TIME = 'datetime', _ARRIVAL = 'arrival', _DEPARTURE = 'departure', _PLACE_ID = 'place_id', @@ -12,25 +12,25 @@ const String _LATITUDE = 'latitude', _DISTANCE = 'distance', _GEO_LOCATION = 'geo_location'; -/// Abstract class to enforce functions -/// to serialize and deserialize an object -abstract class _Serializable { +/// Interface for JSON serialization. +abstract interface class Serializable { Map toJson(); - _Serializable._fromJson(Map json); + Serializable.fromJson(Map json); } -/// Simple abstract class to let the compiler know that an object -/// implementing this class has a location -abstract class _Geospatial { +/// Interface representing a geo location. +abstract interface class GeoSpatial { GeoLocation get geoLocation; } -abstract class _Timestamped { - DateTime get datetime; +/// Interface for timestamped entities. +abstract interface class Timestamped { + DateTime get dateTime; } +/// Utility class for calculating distances. class Distance { - static double fromGeospatial(_Geospatial a, _Geospatial b) { + static double fromGeoSpatial(GeoSpatial a, GeoSpatial b) { return fromList([a.geoLocation.latitude, a.geoLocation.longitude], [b.geoLocation.latitude, b.geoLocation.longitude]); } @@ -52,7 +52,7 @@ class Distance { /// A [GeoLocation] object contains a latitude and longitude /// and represents a 2D spatial coordinates -class GeoLocation implements _Serializable, _Geospatial { +class GeoLocation implements Serializable, GeoSpatial { late double _latitude; late double _longitude; @@ -68,8 +68,10 @@ class GeoLocation implements _Serializable, _Geospatial { double get longitude => _longitude; + @override GeoLocation get geoLocation => this; + @override Map toJson() => {_LATITUDE: latitude, _LONGITUDE: longitude}; @override @@ -78,75 +80,79 @@ class GeoLocation implements _Serializable, _Geospatial { /// A [LocationSample] holds a 2D [GeoLocation] spatial data point /// as well as a [DateTime] value s.t. it may be temporally ordered -class LocationSample implements _Serializable, _Geospatial, _Timestamped { - DateTime _datetime; +class LocationSample implements Serializable, GeoSpatial, Timestamped { + DateTime _dateTime; GeoLocation _geoLocation; - LocationSample(this._geoLocation, this._datetime); + LocationSample(this._geoLocation, this._dateTime); - double? get latitude => geoLocation.latitude; + @override + DateTime get dateTime => _dateTime; - double? get longitude => geoLocation.longitude; + @override + GeoLocation get geoLocation => _geoLocation; - DateTime get datetime => _datetime; + double? get latitude => geoLocation.latitude; - GeoLocation get geoLocation => _geoLocation; + double? get longitude => geoLocation.longitude; + @override Map toJson() => { _GEO_LOCATION: geoLocation.toJson(), - _DATETIME: json.encode(datetime.millisecondsSinceEpoch) + _DATE_TIME: json.encode(dateTime.millisecondsSinceEpoch) }; - factory LocationSample._fromJson(Map json) { + factory LocationSample.fromJson(Map json) { /// Parse, i.e. perform type check - GeoLocation pos = GeoLocation.fromJson(json[_GEO_LOCATION]); - int millis = int.parse(json[_DATETIME]); + GeoLocation pos = + GeoLocation.fromJson(json[_GEO_LOCATION] as Map); + int millis = int.parse(json[_DATE_TIME] as String); DateTime dt = DateTime.fromMillisecondsSinceEpoch(millis); return LocationSample(pos, dt); } LocationSample addNoise() { - double lat = this.geoLocation.latitude * 1.000001; - double lon = this.geoLocation.longitude * 1.000001; - return LocationSample(GeoLocation(lat, lon), this.datetime); + double lat = geoLocation.latitude * 1.000001; + double lon = geoLocation.longitude * 1.000001; + return LocationSample(GeoLocation(lat, lon), dateTime); } @override - String toString() { - return '($latitude, $longitude) @ $_datetime'; - } + String toString() => '($latitude, $longitude) @ $_dateTime'; } -/// A [Stop] represents a cluster of [LocationSample] which were 'close' to eachother +/// A [Stop] represents a cluster of [LocationSample] which were 'close' to each other /// wrt. to Time and 2D space, in a period of little- to no movement. /// A [Stop] has an assigned [placeId] which links it to a [Place]. /// At initialization a stop will be assigned to the 'Noise' place (with id -1), /// and only after all places have been identified will a [Place] be assigned. -class Stop implements _Serializable, _Geospatial, _Timestamped { +class Stop implements Serializable, GeoSpatial, Timestamped { GeoLocation _geoLocation; - int? placeId; + int _placeId = -1; DateTime _arrival, _departure; - Stop._(this._geoLocation, this._arrival, this._departure, - {this.placeId = -1}); + Stop(this._geoLocation, this._arrival, this._departure, [int _placeId = -1]); /// Construct stop from point cloud - factory Stop._fromLocationSamples(List locationSamples, - {int placeId = -1}) { + factory Stop.fromLocationSamples(List locationSamples, + [int placeId = -1]) { // Calculate center GeoLocation center = _computeCentroid(locationSamples); - return Stop._( - center, locationSamples.first.datetime, locationSamples.last.datetime, - placeId: placeId); + return Stop(center, locationSamples.first.dateTime, + locationSamples.last.dateTime, placeId); } + @override + DateTime get dateTime => _arrival; + + @override GeoLocation get geoLocation => _geoLocation; DateTime get departure => _departure; DateTime get arrival => _arrival; - DateTime get datetime => _arrival; + int get placeId => _placeId; List get hourSlots { int startHour = arrival.hour; @@ -182,6 +188,7 @@ class Stop implements _Serializable, _Geospatial, _Timestamped { milliseconds: departure.millisecondsSinceEpoch - arrival.millisecondsSinceEpoch); + @override Map toJson() => { _GEO_LOCATION: geoLocation.toJson(), _PLACE_ID: placeId, @@ -189,28 +196,25 @@ class Stop implements _Serializable, _Geospatial, _Timestamped { _DEPARTURE: departure.millisecondsSinceEpoch }; - factory Stop._fromJson(Map json) { - return Stop._( - GeoLocation.fromJson(json[_GEO_LOCATION]), - DateTime.fromMillisecondsSinceEpoch(json[_ARRIVAL]), - DateTime.fromMillisecondsSinceEpoch(json[_DEPARTURE]), - placeId: json[_PLACE_ID]); - } + factory Stop.fromJson(Map json) => Stop( + GeoLocation.fromJson(json[_GEO_LOCATION] as Map), + DateTime.fromMillisecondsSinceEpoch(json[_ARRIVAL] as int), + DateTime.fromMillisecondsSinceEpoch(json[_DEPARTURE] as int), + json[_PLACE_ID] as int); @override - String toString() { - return 'Stop at place $placeId, (${_geoLocation.toString()}) [$arrival - $departure] (Duration: $duration) '; - } + String toString() => + 'Stop at place $placeId, (${_geoLocation.toString()}) [$arrival - $departure] (Duration: $duration) '; } /// A [Place] is a cluster of [Stop]s found by the DBSCAN algorithm /// https://www.aaai.org/Papers/KDD/1996/KDD96-037.pdf class Place { - int _id; - List _stops; + final int _id; + final List _stops; GeoLocation? _geoLocation; - Place._(this._id, this._stops); + Place(this._id, this._stops); Duration get duration => _stops.map((s) => s.duration).reduce((a, b) => a + b); @@ -218,87 +222,77 @@ class Place { Duration durationForDate(DateTime? d) => _stops .where((s) => s.arrival.midnight == d) .map((s) => s.duration) - .fold(Duration(), (a, b) => a + b); - - GeoLocation? get geoLocation { - if (_geoLocation == null) { - _geoLocation = _computeCentroid(_stops); - } - return _geoLocation; - } + .fold(const Duration(), (a, b) => a + b); int get id => _id; + List get stops => _stops; + + GeoLocation? get geoLocation => _geoLocation ??= _computeCentroid(_stops); + @override - String toString() { - return 'Place ID: $_id, at ${geoLocation.toString()} ($duration)'; - } + String toString() => + 'Place ID: $_id, at ${geoLocation.toString()} ($duration)'; } /// A [Move] is a transfer from one [Stop] to another. /// A set of features can be derived from this such as the haversine distance between /// the stops, the duration of the move, and thereby also the average travel speed. -class Move implements _Serializable, _Timestamped { - Stop _stopFrom, _stopTo; - double? _distance; +class Move implements Serializable, Timestamped { + Stop stopFrom, stopTo; - Move._(this._stopFrom, this._stopTo, this._distance); + /// The haversine distance through all the samples between the two stops + double? distance; + + Move(this.stopFrom, this.stopTo, this.distance); /// Create a Move with a path of samples between two stops - factory Move._fromPath(Stop a, Stop b, List path) { + factory Move.fromPath(Stop a, Stop b, List path) { double d = 0.0; for (int i = 0; i < path.length - 1; i++) { - d += Distance.fromGeospatial(path[i], path[i + 1]); + d += Distance.fromGeoSpatial(path[i], path[i + 1]); } - return Move._(a, b, d); + return Move(a, b, d); } /// Create a Move with a straight line between two stops // ignore: unused_element - factory Move._fromStops(Stop a, Stop b, {double? distance}) { + factory Move.fromStops(Stop a, Stop b, {double? distance}) { /// Distance can be overridden. If it was not then it should be computed - if (distance == null) { - distance = Distance.fromGeospatial(a, b); - } - return Move._(a, b, distance); + distance ??= Distance.fromGeoSpatial(a, b); + return Move(a, b, distance); } - /// The haversine distance through all the samples between the two stops - double? get distance => _distance; - /// The duration of the move in milliseconds Duration get duration => Duration( - milliseconds: _stopTo.arrival.millisecondsSinceEpoch - - _stopFrom.departure.millisecondsSinceEpoch); + milliseconds: stopTo.arrival.millisecondsSinceEpoch - + stopFrom.departure.millisecondsSinceEpoch); /// The average speed when moving between the two places (m/s) double get meanSpeed => distance! / duration.inSeconds.toDouble(); - int? get placeFrom => _stopFrom.placeId; - - int? get placeTo => _stopTo.placeId; + int? get placeFrom => stopFrom.placeId; - Stop get stopFrom => _stopFrom; + int? get placeTo => stopTo.placeId; - Stop get stopTo => _stopTo; - - DateTime get datetime => stopFrom.arrival; + @override + DateTime get dateTime => stopFrom.arrival; + @override Map toJson() => { - _STOP_FROM: _stopFrom.toJson(), - _STOP_TO: _stopTo.toJson(), - _DISTANCE: _distance + _STOP_FROM: stopFrom.toJson(), + _STOP_TO: stopTo.toJson(), + _DISTANCE: distance }; - factory Move._fromJson(Map _json) { - return Move._(Stop._fromJson(_json[_STOP_FROM]), - Stop._fromJson(_json[_STOP_TO]), _json[_DISTANCE]); - } + factory Move.fromJson(Map json) => Move( + Stop.fromJson(json[_STOP_FROM] as Map), + Stop.fromJson(json[_STOP_TO] as Map), + json[_DISTANCE] as double); @override - String toString() { - return 'Move (Place ${_stopFrom.placeId} [${_stopFrom.datetime}] -> Place ${_stopTo.placeId} [${_stopTo.datetime}]) ($duration) (${distance!.toInt()} meters)'; - } + String toString() => + 'Move (Place ${stopFrom.placeId} [${stopFrom.dateTime}] -> Place ${stopTo.placeId} [${stopTo.dateTime}]) ($duration) (${distance!.toInt()} meters)'; } class _HourMatrix { @@ -311,8 +305,8 @@ class _HourMatrix { factory _HourMatrix.fromStops(List stops, int numPlaces) { // Init 2d matrix with 24 rows and cols equal to number of places - List> matrix = new List.generate( - HOURS_IN_A_DAY, (_) => new List.filled(numPlaces, 0.0)); + List> matrix = List.generate( + HOURS_IN_A_DAY, (_) => List.filled(numPlaces, 0.0)); for (int j = 0; j < numPlaces; j++) { List stopsAtPlace = stops.where((s) => (s.placeId) == j).toList(); @@ -367,7 +361,7 @@ class _HourMatrix { double s = 0.0; for (int i = 0; i < HOURS_IN_A_DAY; i++) { for (int j = 0; j < _numberOfPlaces; j++) { - s += this.matrix[i][j]; + s += matrix[i][j]; } } return s; @@ -379,7 +373,7 @@ class _HourMatrix { assert(other.matrix.length == HOURS_IN_A_DAY && other.matrix.first.length == _matrix.first.length); - double maxOverlap = min(this.sum, other.sum); + double maxOverlap = min(sum, other.sum); if (maxOverlap == 0.0) return -1.0; @@ -391,8 +385,8 @@ class _HourMatrix { /// If overlap in time-place matrix, /// add the overlap to the total overlap. /// The overlap is equal to the minimum of the two quantities - if (this.matrix[i][j] >= 0.0 && other.matrix[i][j] >= 0.0) { - overlap += min(this.matrix[i][j], other.matrix[i][j]); + if (matrix[i][j] >= 0.0 && other.matrix[i][j] >= 0.0) { + overlap += min(matrix[i][j], other.matrix[i][j]); } } } diff --git a/packages/mobility_features/lib/src/mobility_features.dart b/packages/mobility_features/lib/src/features.dart similarity index 77% rename from packages/mobility_features/lib/src/mobility_features.dart rename to packages/mobility_features/lib/src/features.dart index 130a3d00a..c97d5f8c5 100644 --- a/packages/mobility_features/lib/src/mobility_features.dart +++ b/packages/mobility_features/lib/src/features.dart @@ -1,15 +1,15 @@ -part of mobility_features; +part of '../mobility_features.dart'; /// Main entry for configuring and listening for mobility features. -/// Used as a singleton `MobilityFactory()`. +/// Used as a singleton `MobilityFeatures()`. class MobilityFeatures { double _stopRadius = 5, _placeRadius = 50; Duration _stopDuration = const Duration(seconds: 20); StreamSubscription? _subscription; - _MobilitySerializer? _serializerSamples; - late _MobilitySerializer _serializerStops; - late _MobilitySerializer _serializerMoves; + MobilitySerializer? _serializerSamples; + late MobilitySerializer _serializerStops; + late MobilitySerializer _serializerMoves; List _stops = []; List _moves = []; List _places = []; @@ -24,8 +24,7 @@ class MobilityFeatures { } // Outgoing stream - StreamController _streamController = - StreamController.broadcast(); + final _streamController = StreamController.broadcast(); Stream get contextStream => _streamController.stream; @@ -35,26 +34,26 @@ class MobilityFeatures { // Private Singleton field static final MobilityFeatures _instance = MobilityFeatures._(); - /// Public getter for the Singleton instance + /// Singleton instance of MobilityFeatures. factory MobilityFeatures() => _instance; /// Listen to a Stream of [LocationSample]. /// The subscription will be stored as a [StreamSubscription] /// which may be cancelled later. - Future startListening(Stream stream) async { + Future startListening(Stream stream) async { await _handleInit(); if (_subscription != null) { await _subscription!.cancel(); } - _subscription = await stream.listen(_onData); + _subscription = stream.listen(_onData); } Future _handleInit() async { _serializerSamples = - _serializerSamples = _MobilitySerializer(); - _serializerStops = _MobilitySerializer(); - _serializerMoves = _MobilitySerializer(); + _serializerSamples = MobilitySerializer(); + _serializerStops = MobilitySerializer(); + _serializerMoves = MobilitySerializer(); _stops = (await _serializerStops.load() as List); _moves = (await _serializerMoves.load() as List); @@ -62,8 +61,9 @@ class MobilityFeatures { _stops = uniqueElements(_stops) as List; _moves = uniqueElements(_moves) as List; - if (_cluster.isNotEmpty) + if (_cluster.isNotEmpty) { _print('Loaded ${_cluster.length} location samples from disk'); + } if (_stops.isNotEmpty) { _print('Loaded ${_stops.length} stops from disk'); } @@ -71,11 +71,9 @@ class MobilityFeatures { _print('Loaded ${_moves.length} moves from disk'); } - for (var s in _stops) _print(s); - if (_stops.isNotEmpty) { /// Only keeps stops and moves from the last known date - DateTime date = _stops.last.datetime.midnight; + DateTime date = _stops.last.dateTime.midnight; _stops = _getElementsForDate(_stops, date) as List; _moves = _getElementsForDate(_moves, date) as List; _places = _findPlaces(_stops, placeRadius: _placeRadius); @@ -88,7 +86,7 @@ class MobilityFeatures { } /// Cancel the [StreamSubscription] and stop listening. - Future stopListening() async { + Future stopListening() async { if (_subscription != null) { await _subscription!.cancel(); } @@ -111,7 +109,7 @@ class MobilityFeatures { // If previous samples exist, check if we should compute anything if (_cluster.isNotEmpty) { // If previous sample was on a different date, reset everything - if (_cluster.last.datetime.midnight != sample.datetime.midnight) { + if (_cluster.last.dateTime.midnight != sample.dateTime.midnight) { _createStopAndResetCluster(); _clearEverything(); } @@ -122,7 +120,7 @@ class MobilityFeatures { GeoLocation centroid = _computeCentroid(_cluster); // If the new data point is far away from cluster, make stop - if (Distance.fromGeospatial(centroid, sample) > _stopRadius) { + if (Distance.fromGeoSpatial(centroid, sample) > _stopRadius) { _createStopAndResetCluster(); } } @@ -154,7 +152,7 @@ class MobilityFeatures { /// Converts the cluster into a stop, i.e. closing the cluster void _createStopAndResetCluster() { - Stop s = Stop._fromLocationSamples(_cluster); + Stop s = Stop.fromLocationSamples(_cluster); // If the stop is too short, it is discarded // Otherwise compute a context and send it via the stream @@ -176,7 +174,7 @@ class MobilityFeatures { _serializerStops.save(_stops); // Extract date - DateTime date = _cluster.last.datetime.midnight; + DateTime date = _cluster.last.dateTime.midnight; if (stopPrev != null) { _moves = _findMoves(_stops, _samples); @@ -207,15 +205,11 @@ class MobilityFeatures { } /// Configure the stop-radius for the place algorithm - set placeRadius(value) { - _placeRadius = value; - } + set placeRadius(double value) => _placeRadius = value; - Future<_MobilitySerializer> + Future> get _locationSampleSerializer async { - if (_serializerSamples == null) { - _serializerSamples = _MobilitySerializer(); - } + _serializerSamples ??= MobilitySerializer(); return _serializerSamples!; } @@ -229,18 +223,18 @@ class MobilityFeatures { return (await serializer.load() as List); } - static List<_Timestamped> _getElementsForDate( - List<_Timestamped> elements, DateTime date) { - return elements.where((e) => e.datetime.midnight == date).toList(); + static List _getElementsForDate( + List elements, DateTime date) { + return elements.where((e) => e.dateTime.midnight == date).toList(); } - static List<_Timestamped> uniqueElements(List<_Timestamped> elements) { + static List uniqueElements(List elements) { List seen = []; - elements.sort((a, b) => a.datetime.compareTo(b.datetime)); + elements.sort((a, b) => a.dateTime.compareTo(b.dateTime)); return elements.where((e) { - int ms = e.datetime.millisecondsSinceEpoch; + int ms = e.dateTime.millisecondsSinceEpoch; if (!seen.contains(ms)) { seen.add(ms); return true; diff --git a/packages/mobility_features/lib/src/mobility_file_util.dart b/packages/mobility_features/lib/src/file_util.dart similarity index 92% rename from packages/mobility_features/lib/src/mobility_file_util.dart rename to packages/mobility_features/lib/src/file_util.dart index c1c9b4cc0..4638589c1 100644 --- a/packages/mobility_features/lib/src/mobility_file_util.dart +++ b/packages/mobility_features/lib/src/file_util.dart @@ -1,4 +1,4 @@ -part of mobility_features; +part of '../mobility_features.dart'; const String _LOCATION_SAMPLES_FILE = 'location_samples', _STOPS_FILE = 'stops', @@ -29,7 +29,7 @@ Future _fileReference(Type T) async { } // Create a file reference - File reference = new File('$path/$type.json'); + File reference = File('$path/$type.json'); // If it does not exist already, // create it by writing an empty string to it diff --git a/packages/mobility_features/lib/src/mobility_functions.dart b/packages/mobility_features/lib/src/functions.dart similarity index 65% rename from packages/mobility_features/lib/src/mobility_functions.dart rename to packages/mobility_features/lib/src/functions.dart index 7cfb0c566..b08b71718 100644 --- a/packages/mobility_features/lib/src/mobility_functions.dart +++ b/packages/mobility_features/lib/src/functions.dart @@ -1,4 +1,4 @@ -part of mobility_features; +part of '../mobility_features.dart'; /// Returns an [Iterable] of [List]s where the nth element in the returned /// iterable contains the nth element from every Iterable in [iterables]. The @@ -25,21 +25,15 @@ Iterable range(int low, int high) sync* { extension CompareDates on DateTime { bool geq(DateTime other) { - return this.isAfter(other) || this.isAtSameMomentAs(other); + return isAfter(other) || isAtSameMomentAs(other); } bool leq(DateTime other) { - return this.isBefore(other) || this.isAtSameMomentAs(other); + return isBefore(other) || isAtSameMomentAs(other); } DateTime get midnight { - return DateTime(this.year, this.month, this.day); - } -} - -extension AverageIterable on Iterable { - double? get mean { - return this.fold(0, (dynamic a, b) => a + b) / this.length.toDouble(); + return DateTime(year, month, day); } } @@ -69,59 +63,49 @@ int argmaxInt(List list) { return i; } -void printMatrix(List m) { - for (List row in m) { - String s = ''; - for (var e in row) { - s += '$e '; - } - print(s); - } -} - -List> zeroMatrix(int rows, int cols) { - return new List.generate(rows, (_) => new List.filled(cols, 0.0)); -} +List> zeroMatrix(int rows, int cols) => + List.generate(rows, (_) => List.filled(cols, 0.0)); List _mergeStops(List stops) { List merged = []; if (stops.length < 2) return stops; - /// Should be applied after places have been found + // Should be applied after places have been found List toMerge = []; - void _merge() { + void merge() { if (toMerge.isEmpty) return; GeoLocation geoLocation = _computeCentroid(toMerge); DateTime arr = toMerge.first.arrival; DateTime dep = toMerge.last.departure; - Stop s = Stop._(geoLocation, arr, dep, placeId: toMerge.first.placeId); + Stop s = Stop(geoLocation, arr, dep, toMerge.first.placeId); merged.add(s); toMerge = []; } for (Stop stop in stops) { - /// If stop is noisy, just add it to the merged list, dont do anything to it + // If stop is noisy, just add it to the merged list, don't do anything to it if (stop.placeId == -1) { merged.add(stop); } else { - /// If no stops to merge, we cannot merge and we therefore add the current - /// stop and go to the next one + // If no stops to merge, we cannot merge and we therefore add the current + // stop and go to the next one if (toMerge.isEmpty) { toMerge.add(stop); } - /// Otherwise check if we should add it or merge + // Otherwise check if we should add it or merge else { if (stop.placeId != toMerge.last.placeId) { - _merge(); + merge(); } toMerge.add(stop); } } } - /// Merge remaining stops in the toMerge list - _merge(); + // Merge remaining stops in the toMerge list + merge(); + return merged; } diff --git a/packages/mobility_features/lib/src/mobility_intermediate.dart b/packages/mobility_features/lib/src/intermediate.dart similarity index 89% rename from packages/mobility_features/lib/src/mobility_intermediate.dart rename to packages/mobility_features/lib/src/intermediate.dart index 75723927c..f72e61138 100644 --- a/packages/mobility_features/lib/src/mobility_intermediate.dart +++ b/packages/mobility_features/lib/src/intermediate.dart @@ -1,4 +1,4 @@ -part of mobility_features; +part of '../mobility_features.dart'; /// Find the stops in a sequence of gps data points //List _findStops(List data, @@ -66,11 +66,13 @@ List _findPlaces(List stops, {double placeRadius = 50.0}) { List stopsForPlace = indices.map((i) => (stops[i])).toList(); /// Add place to the list - Place p = Place._(label, stopsForPlace); + Place p = Place(label, stopsForPlace); places.add(p); /// Set placeId field for the stops belonging to this place - stopsForPlace.forEach((s) => s.placeId = p._id); + for (var s in stopsForPlace) { + s._placeId = p.id; + } } return places; } @@ -83,10 +85,10 @@ List _findMoves(List stops, List samples) { if (previous != null) { final path = samples .where((s) => - previous!.datetime.leq(s.datetime) && - previous.datetime.geq(s.datetime)) + previous!.dateTime.leq(s.dateTime) && + previous.dateTime.geq(s.dateTime)) .toList(); - Move m = Move._fromPath(previous, current, path); + Move m = Move.fromPath(previous, current, path); moves.add(m); } previous = current; @@ -94,12 +96,13 @@ List _findMoves(List stops, List samples) { return moves; } -GeoLocation _computeCentroid(List<_Geospatial> data) { +GeoLocation _computeCentroid(List data) { double lat = Stats.fromData(data.map((d) => (d.geoLocation.latitude)).toList()).median as double; double lon = Stats.fromData(data.map((d) => (d.geoLocation.longitude)).toList()).median as double; + return GeoLocation(lat, lon); } diff --git a/packages/mobility_features/lib/src/mobility_serializer.dart b/packages/mobility_features/lib/src/mobility_serializer.dart deleted file mode 100644 index 826bbc31f..000000000 --- a/packages/mobility_features/lib/src/mobility_serializer.dart +++ /dev/null @@ -1,65 +0,0 @@ -part of mobility_features; - -class _MobilitySerializer { - /// Provide a file reference in order to serialize objects. - File? _file; - String delimiter = '\n'; - - _MobilitySerializer(); - - /// Deletes the content of the file - void flush() { - _file!.writeAsStringSync('', mode: FileMode.write); - } - - Future get file async { - if (_file == null) { - _file = await _fileReference(T); - } - return _file!; - } - - /// Writes a list of [_Serializable] to the file given in the constructor. - void save(List<_Serializable> elements) async { - File f = await file; - String jsonString = ""; - for (_Serializable e in elements) { - jsonString += json.encode(e.toJson()) + delimiter; - } - f.writeAsStringSync(jsonString, mode: FileMode.writeOnlyAppend); - } - - /// Reads contents of the file in the constructor, - /// and maps it to a list of a specific [_Serializable] type. - Future> load() async { - File f = await file; - - /// Read file content as one big string - String content = await f.readAsString(); - - /// Split content into lines by delimiting them - List lines = content.split(delimiter); - - /// Remove last entry since it is always empty - /// Then convert each line to JSON, and then to Dart Map objects - Iterable> jsonObjs = lines - .sublist(0, lines.length - 1) - .map((e) => json.decode(e)) - .map((e) => Map.from(e)); - - switch (T) { - case Move: - - /// Filter out moves which are not recent - return jsonObjs.map((x) => Move._fromJson(x)).toList(); - case Stop: - - /// Filter out stops which are not recent - return jsonObjs.map((x) => Stop._fromJson(x)).toList(); - default: - - /// Filter out data samples not from today - return jsonObjs.map((x) => LocationSample._fromJson(x)).toList(); - } - } -} diff --git a/packages/mobility_features/lib/src/serializer.dart b/packages/mobility_features/lib/src/serializer.dart new file mode 100644 index 000000000..5bef71c57 --- /dev/null +++ b/packages/mobility_features/lib/src/serializer.dart @@ -0,0 +1,55 @@ +part of '../mobility_features.dart'; + +class MobilitySerializer { + // Provide a file reference in order to serialize objects. + File? _file; + String delimiter = '\n'; + + MobilitySerializer(); + + /// Deletes the content of the file + void flush() => _file!.writeAsStringSync('', mode: FileMode.write); + + Future get file async => _file ??= await _fileReference(T); + + /// Writes a list of [Serializable] to the file given in the constructor. + void save(List elements) async { + File f = await file; + String jsonString = ""; + for (Serializable e in elements) { + jsonString += json.encode(e.toJson()) + delimiter; + } + f.writeAsStringSync(jsonString, mode: FileMode.writeOnlyAppend); + } + + /// Reads contents of the [file] and maps it to a list of a specific + /// [Serializable] type. + Future> load() async { + File f = await file; + + // Read file content as one big string + String content = await f.readAsString(); + + // Split content into lines by delimiting them + List lines = content.split(delimiter); + + // Remove last entry since it is always empty + // Then convert each line to JSON, and then to Dart Map objects + Iterable> jsonObjs = lines + .sublist(0, lines.length - 1) + .map((e) => json.decode(e)) + .map((e) => Map.from(e as Map)); + + switch (T) { + // Filter out moves which are not recent + case const (Move): + return jsonObjs.map((x) => Move.fromJson(x)).toList(); + // Filter out stops which are not recent + case const (Stop): + return jsonObjs.map((x) => Stop.fromJson(x)).toList(); + // Filter out data samples not from today + default: + return jsonObjs.map((x) => LocationSample.fromJson(x)).toList(); + } + } +} diff --git a/packages/mobility_features/pubspec.yaml b/packages/mobility_features/pubspec.yaml index 51098a6bf..e438b2b76 100644 --- a/packages/mobility_features/pubspec.yaml +++ b/packages/mobility_features/pubspec.yaml @@ -1,10 +1,11 @@ name: mobility_features description: Calculation of real-time mobility features like places, stops, and home stay -version: 4.0.1 +version: 5.0.0 homepage: https://github.com/cph-cachet/flutter-plugins/ environment: - sdk: ">=2.17.0 <4.0.0" + sdk: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" dependencies: flutter: @@ -16,5 +17,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + flutter_lints: any flutter: diff --git a/packages/mobility_features/test/mobility_features_test.dart b/packages/mobility_features/test/mobility_features_test.dart index df215cbf3..542b589a8 100644 --- a/packages/mobility_features/test/mobility_features_test.dart +++ b/packages/mobility_features/test/mobility_features_test.dart @@ -9,7 +9,7 @@ import 'dart:convert'; part 'test_utils.dart'; void main() async { - JsonEncoder jsonEncoder = JsonEncoder.withIndent('\t'); + JsonEncoder jsonEncoder = const JsonEncoder.withIndent('\t'); DateTime jan01 = DateTime(2020, 01, 01); // Poppelgade 7, home @@ -64,11 +64,11 @@ void main() async { /// Todays data List locationSamples = [ // 5 hours spent at home - LocationSample(pos1, date.add(Duration(hours: 0, minutes: 0))), - LocationSample(pos1, date.add(Duration(hours: 6, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 0, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 6, minutes: 0))), - LocationSample(pos2, date.add(Duration(hours: 8, minutes: 0))), - LocationSample(pos2, date.add(Duration(hours: 9, minutes: 30))), + LocationSample(pos2, date.add(const Duration(hours: 8, minutes: 0))), + LocationSample(pos2, date.add(const Duration(hours: 9, minutes: 30))), ]; /// Save @@ -87,15 +87,15 @@ void main() async { List samples = [ // 5 hours spent at home - LocationSample(pos1, jan01.add(Duration(hours: 0, minutes: 0))), - LocationSample(pos1, jan01.add(Duration(hours: 6, minutes: 0))), + LocationSample(pos1, jan01.add(const Duration(hours: 0, minutes: 0))), + LocationSample(pos1, jan01.add(const Duration(hours: 6, minutes: 0))), - LocationSample(pos2, jan01.add(Duration(hours: 8, minutes: 0))), - LocationSample(pos2, jan01.add(Duration(hours: 9, minutes: 0))), + LocationSample(pos2, jan01.add(const Duration(hours: 8, minutes: 0))), + LocationSample(pos2, jan01.add(const Duration(hours: 9, minutes: 0))), - LocationSample(pos1, jan01.add(Duration(hours: 21, minutes: 0))), - LocationSample( - pos1, jan01.add(Duration(hours: 23, minutes: 59, seconds: 59))), + LocationSample(pos1, jan01.add(const Duration(hours: 21, minutes: 0))), + LocationSample(pos1, + jan01.add(const Duration(hours: 23, minutes: 59, seconds: 59))), ]; await MobilityFeatures().saveSamples(samples); @@ -109,38 +109,38 @@ void main() async { List samplesNoDuplicates = [ // 5 hours spent at home - LocationSample(pos1, jan01.add(Duration(hours: 0, minutes: 0))), - LocationSample(pos1, jan01.add(Duration(hours: 6, minutes: 0))), + LocationSample(pos1, jan01.add(const Duration(hours: 0, minutes: 0))), + LocationSample(pos1, jan01.add(const Duration(hours: 6, minutes: 0))), - LocationSample(pos2, jan01.add(Duration(hours: 8, minutes: 0))), - LocationSample(pos2, jan01.add(Duration(hours: 9, minutes: 0))), + LocationSample(pos2, jan01.add(const Duration(hours: 8, minutes: 0))), + LocationSample(pos2, jan01.add(const Duration(hours: 9, minutes: 0))), - LocationSample(pos1, jan01.add(Duration(hours: 21, minutes: 0))), - LocationSample( - pos1, jan01.add(Duration(hours: 23, minutes: 59, seconds: 59))), + LocationSample(pos1, jan01.add(const Duration(hours: 21, minutes: 0))), + LocationSample(pos1, + jan01.add(const Duration(hours: 23, minutes: 59, seconds: 59))), ]; /// Todays data List samplesWithDuplicates = [ // 5 hours spent at home - LocationSample(pos1, jan01.add(Duration(hours: 0, minutes: 0))), - LocationSample(pos1, jan01.add(Duration(hours: 6, minutes: 0))), - LocationSample(pos1, jan01.add(Duration(hours: 6, minutes: 0))), - LocationSample(pos1, jan01.add(Duration(hours: 6, minutes: 0))), - - LocationSample(pos2, jan01.add(Duration(hours: 8, minutes: 0))), - LocationSample(pos2, jan01.add(Duration(hours: 8, minutes: 0))), - LocationSample(pos2, jan01.add(Duration(hours: 8, minutes: 0))), - LocationSample(pos2, jan01.add(Duration(hours: 9, minutes: 0))), - - LocationSample(pos1, jan01.add(Duration(hours: 0, minutes: 0))), - LocationSample(pos1, jan01.add(Duration(hours: 6, minutes: 0))), - LocationSample(pos1, jan01.add(Duration(hours: 6, minutes: 0))), - LocationSample(pos1, jan01.add(Duration(hours: 6, minutes: 0))), - - LocationSample(pos1, jan01.add(Duration(hours: 21, minutes: 0))), - LocationSample( - pos1, jan01.add(Duration(hours: 23, minutes: 59, seconds: 59))), + LocationSample(pos1, jan01.add(const Duration(hours: 0, minutes: 0))), + LocationSample(pos1, jan01.add(const Duration(hours: 6, minutes: 0))), + LocationSample(pos1, jan01.add(const Duration(hours: 6, minutes: 0))), + LocationSample(pos1, jan01.add(const Duration(hours: 6, minutes: 0))), + + LocationSample(pos2, jan01.add(const Duration(hours: 8, minutes: 0))), + LocationSample(pos2, jan01.add(const Duration(hours: 8, minutes: 0))), + LocationSample(pos2, jan01.add(const Duration(hours: 8, minutes: 0))), + LocationSample(pos2, jan01.add(const Duration(hours: 9, minutes: 0))), + + LocationSample(pos1, jan01.add(const Duration(hours: 0, minutes: 0))), + LocationSample(pos1, jan01.add(const Duration(hours: 6, minutes: 0))), + LocationSample(pos1, jan01.add(const Duration(hours: 6, minutes: 0))), + LocationSample(pos1, jan01.add(const Duration(hours: 6, minutes: 0))), + + LocationSample(pos1, jan01.add(const Duration(hours: 21, minutes: 0))), + LocationSample(pos1, + jan01.add(const Duration(hours: 23, minutes: 59, seconds: 59))), ]; final samples = MobilityFeatures.uniqueElements(samplesWithDuplicates); @@ -152,17 +152,17 @@ void main() async { List samples = [ /// Location 1 - LocationSample(pos1, jan01.add(Duration(hours: 0, minutes: 0))), - LocationSample(pos1, jan01.add(Duration(hours: 6, minutes: 0))), + LocationSample(pos1, jan01.add(const Duration(hours: 0, minutes: 0))), + LocationSample(pos1, jan01.add(const Duration(hours: 6, minutes: 0))), /// Location 2 - LocationSample(pos2, jan01.add(Duration(hours: 8, minutes: 0))), - LocationSample(pos2, jan01.add(Duration(hours: 9, minutes: 0))), + LocationSample(pos2, jan01.add(const Duration(hours: 8, minutes: 0))), + LocationSample(pos2, jan01.add(const Duration(hours: 9, minutes: 0))), /// Location 1 - LocationSample(pos1, jan01.add(Duration(hours: 21, minutes: 0))), - LocationSample( - pos1, jan01.add(Duration(hours: 23, minutes: 59, seconds: 59))), + LocationSample(pos1, jan01.add(const Duration(hours: 21, minutes: 0))), + LocationSample(pos1, + jan01.add(const Duration(hours: 23, minutes: 59, seconds: 59))), ]; // Save samples, one by one @@ -181,30 +181,31 @@ void main() async { List samples = [ /// Location 1 (Home) - LocationSample(pos1, date.add(Duration(hours: 0, minutes: 0))), - LocationSample(pos1, date.add(Duration(hours: 1, minutes: 0))), - LocationSample(pos1, date.add(Duration(hours: 2, minutes: 0))), - LocationSample(pos1, date.add(Duration(hours: 8, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 0, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 1, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 2, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 8, minutes: 0))), /// end of stop 1 /// Location 2 - LocationSample(pos2, date.add(Duration(hours: 8, minutes: 30))), - LocationSample(pos2, date.add(Duration(hours: 9, minutes: 30))), - LocationSample(pos2, date.add(Duration(hours: 12, minutes: 30))), + LocationSample(pos2, date.add(const Duration(hours: 8, minutes: 30))), + LocationSample(pos2, date.add(const Duration(hours: 9, minutes: 30))), + LocationSample(pos2, date.add(const Duration(hours: 12, minutes: 30))), /// end of stop 2 /// Gap in data // Location 1 (Home) - LocationSample(pos1, date.add(Duration(hours: 16, seconds: 1))), - LocationSample(pos1, date.add(Duration(hours: 20, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 16, seconds: 1))), + LocationSample(pos1, date.add(const Duration(hours: 20, minutes: 0))), /// end of stop 3 // Location 0 (Home), New day - LocationSample(pos1, date.add(Duration(days: 1, hours: 0, minutes: 2))), + LocationSample( + pos1, date.add(const Duration(days: 1, hours: 0, minutes: 2))), ]; // Create stream controller to stream the individual samples @@ -242,57 +243,58 @@ void main() async { List samples = [ /// Location 1 (Home) - LocationSample(pos1, date.add(Duration(hours: 0, minutes: 0))), - LocationSample(pos1, date.add(Duration(hours: 1, minutes: 0))), - LocationSample(pos1, date.add(Duration(hours: 2, minutes: 0))), - LocationSample(pos1, date.add(Duration(hours: 8, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 0, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 1, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 2, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 8, minutes: 0))), /// end of stop 1 /// Path to Location 1 LocationSample(GeoLocation(55.691806, 12.557528), - date.add(Duration(hours: 8, minutes: 1))), + date.add(const Duration(hours: 8, minutes: 1))), LocationSample(GeoLocation(55.691419, 12.556970), - date.add(Duration(hours: 8, minutes: 2))), + date.add(const Duration(hours: 8, minutes: 2))), LocationSample(GeoLocation(55.691081, 12.556455), - date.add(Duration(hours: 8, minutes: 3))), + date.add(const Duration(hours: 8, minutes: 3))), LocationSample(GeoLocation(55.690706, 12.555875), - date.add(Duration(hours: 8, minutes: 4))), + date.add(const Duration(hours: 8, minutes: 4))), LocationSample(GeoLocation(55.690434, 12.555457), - date.add(Duration(hours: 8, minutes: 5))), + date.add(const Duration(hours: 8, minutes: 5))), LocationSample(GeoLocation(55.690161, 12.555060), - date.add(Duration(hours: 8, minutes: 6))), + date.add(const Duration(hours: 8, minutes: 6))), LocationSample(GeoLocation(55.690542, 12.554481), - date.add(Duration(hours: 8, minutes: 7))), + date.add(const Duration(hours: 8, minutes: 7))), LocationSample(GeoLocation(55.690801, 12.550164), - date.add(Duration(hours: 8, minutes: 8))), + date.add(const Duration(hours: 8, minutes: 8))), LocationSample(GeoLocation(55.690825, 12.544910), - date.add(Duration(hours: 8, minutes: 9))), + date.add(const Duration(hours: 8, minutes: 9))), LocationSample(GeoLocation(55.689685, 12.543661), - date.add(Duration(hours: 8, minutes: 15))), + date.add(const Duration(hours: 8, minutes: 15))), LocationSample(GeoLocation(55.688083, 12.541852), - date.add(Duration(hours: 8, minutes: 20))), + date.add(const Duration(hours: 8, minutes: 20))), LocationSample(GeoLocation(55.686007, 12.539484), - date.add(Duration(hours: 8, minutes: 29))), + date.add(const Duration(hours: 8, minutes: 29))), /// Location 1 - LocationSample(pos2, date.add(Duration(hours: 8, minutes: 30))), - LocationSample(pos2, date.add(Duration(hours: 10, minutes: 30))), - LocationSample(pos2, date.add(Duration(hours: 11, minutes: 30))), - LocationSample(pos2, date.add(Duration(hours: 12, minutes: 30))), + LocationSample(pos2, date.add(const Duration(hours: 8, minutes: 30))), + LocationSample(pos2, date.add(const Duration(hours: 10, minutes: 30))), + LocationSample(pos2, date.add(const Duration(hours: 11, minutes: 30))), + LocationSample(pos2, date.add(const Duration(hours: 12, minutes: 30))), /// end of stop 2 /// Gap in data from 12:30 to 16:00 /// Location 0 (Home) - LocationSample(pos1, date.add(Duration(hours: 16, seconds: 1))), - LocationSample(pos1, date.add(Duration(hours: 20, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 16, seconds: 1))), + LocationSample(pos1, date.add(const Duration(hours: 20, minutes: 0))), /// end of stop 3 /// Location 0 (Home), New day - LocationSample(pos1, date.add(Duration(days: 1, hours: 0, minutes: 2))), + LocationSample( + pos1, date.add(const Duration(days: 1, hours: 0, minutes: 2))), ]; /// Create stream controller to stream the individual samples @@ -322,44 +324,45 @@ void main() async { List samples = [ /// Location 1 (Home) - LocationSample(pos1, date.add(Duration(hours: 0, minutes: 0))), - LocationSample(pos1, date.add(Duration(hours: 1, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 0, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 1, minutes: 0))), /// end of stop 1 - LocationSample(pos1, date.add(Duration(hours: 2, minutes: 0))) + LocationSample(pos1, date.add(const Duration(hours: 2, minutes: 0))) .addNoise(), - LocationSample(pos1, date.add(Duration(hours: 8, minutes: 0))) + LocationSample(pos1, date.add(const Duration(hours: 8, minutes: 0))) .addNoise(), /// end of stop 2 /// Location 1 - LocationSample(pos2, date.add(Duration(hours: 8, minutes: 30))), - LocationSample(pos2, date.add(Duration(hours: 9, minutes: 30))), + LocationSample(pos2, date.add(const Duration(hours: 8, minutes: 30))), + LocationSample(pos2, date.add(const Duration(hours: 9, minutes: 30))), /// end of stop 3 - LocationSample(pos2, date.add(Duration(hours: 10, minutes: 30))) + LocationSample(pos2, date.add(const Duration(hours: 10, minutes: 30))) .addNoise(), - LocationSample(pos2, date.add(Duration(hours: 11, minutes: 30))) + LocationSample(pos2, date.add(const Duration(hours: 11, minutes: 30))) .addNoise(), /// end of stop 4 - LocationSample(pos2, date.add(Duration(hours: 12, minutes: 00))), - LocationSample(pos2, date.add(Duration(hours: 12, minutes: 15))), - LocationSample(pos2, date.add(Duration(hours: 12, minutes: 30))), + LocationSample(pos2, date.add(const Duration(hours: 12, minutes: 00))), + LocationSample(pos2, date.add(const Duration(hours: 12, minutes: 15))), + LocationSample(pos2, date.add(const Duration(hours: 12, minutes: 30))), /// end of stop 5 /// Gap in data (should get interpolated to Location 1) /// Location 0 (Home) - LocationSample(pos1, date.add(Duration(hours: 16, seconds: 1))), - LocationSample(pos1, date.add(Duration(hours: 20, minutes: 0))), + LocationSample(pos1, date.add(const Duration(hours: 16, seconds: 1))), + LocationSample(pos1, date.add(const Duration(hours: 20, minutes: 0))), /// end of stop 6 /// Location 0 (Home), New day - LocationSample(pos1, date.add(Duration(days: 1, hours: 0, minutes: 2))), + LocationSample( + pos1, date.add(const Duration(days: 1, hours: 0, minutes: 2))), ]; /// Create stream controller to stream the individual samples @@ -390,11 +393,11 @@ void main() async { final samples = loadDataSet(); print(samples.length); - final dates = samples.map((e) => e.datetime.midnight).toSet().toList(); + final dates = samples.map((e) => e.dateTime.midnight).toSet().toList(); printList(dates); final onLastDate = - samples.where((e) => e.datetime.midnight == dates[1]).toList(); + samples.where((e) => e.dateTime.midnight == dates[1]).toList(); print(onLastDate.length); print(onLastDate.last); diff --git a/packages/mobility_features/test/test_utils.dart b/packages/mobility_features/test/test_utils.dart index 3aa0cf25f..a7606115a 100644 --- a/packages/mobility_features/test/test_utils.dart +++ b/packages/mobility_features/test/test_utils.dart @@ -1,4 +1,4 @@ -part of mobility_test; +part of 'mobility_features_test.dart'; const String datasetPath = 'lib/data/example-multi.json'; const String testDataDir = 'test/testdata'; @@ -10,16 +10,16 @@ Duration takeTime(DateTime start, DateTime end) { /// Clean file every time test is run void flushFiles() async { - File samples = new File('$testDataDir/location_samples.json'); - File stops = new File('$testDataDir/stops.json'); - File moves = new File('$testDataDir/moves.json'); + File samples = File('$testDataDir/location_samples.json'); + File stops = File('$testDataDir/stops.json'); + File moves = File('$testDataDir/moves.json'); await samples.writeAsString(''); await stops.writeAsString(''); await moves.writeAsString(''); } -void printList(List l) { +void printList(List l) { for (int i = 0; i < l.length; i++) { print('[$i] ${l[i]}'); } @@ -36,20 +36,22 @@ class LocationDTO { } List loadDataSet() { - File f = new File('$testDataDir/data-example-munich.json'); + File f = File('$testDataDir/data-example-munich.json'); String content = f.readAsStringSync(); List lines = content.split('\n'); List samples = []; - lines.forEach((e) { + for (var e in lines) { try { - Map m = json.decode(e); - GeoLocation geoLocation = - GeoLocation(double.parse(m['lat']), double.parse(m['lon'])); - DateTime dateTime = - DateTime.fromMillisecondsSinceEpoch(int.parse(m['datetime'])); + Map m = json.decode(e) as Map; + GeoLocation geoLocation = GeoLocation( + double.parse(m['lat'] as String), double.parse(m['lon'] as String)); + DateTime dateTime = DateTime.fromMillisecondsSinceEpoch( + int.parse(m['datetime'] as String)); samples.add(LocationSample(geoLocation, dateTime)); - } catch (error) {} - }); + } catch (error) { + print('ERROR - $error'); + } + } return samples; } diff --git a/packages/mobility_features/test/testdata/location_samples.json b/packages/mobility_features/test/testdata/location_samples.json index 3f290383b..1a90ddbb4 100644 --- a/packages/mobility_features/test/testdata/location_samples.json +++ b/packages/mobility_features/test/testdata/location_samples.json @@ -88,3 +88,23 @@ {"geo_location":{"latitude":48.171306756445375,"longitude":11.563214587831721},"datetime":"1581633712549"} {"geo_location":{"latitude":48.17129139009002,"longitude":11.563199191004612},"datetime":"1581633718547"} {"geo_location":{"latitude":48.17131649786042,"longitude":11.56319249730382},"datetime":"1581633724548"} +{"geo_location":{"latitude":48.17130982924529,"longitude":11.563208418153053},"datetime":"1581633730535"} +{"geo_location":{"latitude":48.17130694149507,"longitude":11.56320032456987},"datetime":"1581633736552"} +{"geo_location":{"latitude":48.17132068611235,"longitude":11.563178822329546},"datetime":"1581633742547"} +{"geo_location":{"latitude":48.17132463655833,"longitude":11.563177082885625},"datetime":"1581633748554"} +{"geo_location":{"latitude":48.17131253315505,"longitude":11.56319005365194},"datetime":"1581633754538"} +{"geo_location":{"latitude":48.17131800130947,"longitude":11.563182633985699},"datetime":"1581633760552"} +{"geo_location":{"latitude":48.17131755621913,"longitude":11.563167472268956},"datetime":"1581633766560"} +{"geo_location":{"latitude":48.17131626593944,"longitude":11.563175580431988},"datetime":"1581633772555"} +{"geo_location":{"latitude":48.17129311805048,"longitude":11.563180197355466},"datetime":"1581633778547"} +{"geo_location":{"latitude":48.17129965259196,"longitude":11.563202551545556},"datetime":"1581633784560"} +{"geo_location":{"latitude":48.17130379171522,"longitude":11.563161991515006},"datetime":"1581633790544"} +{"geo_location":{"latitude":48.17130874979778,"longitude":11.563167560772891},"datetime":"1581633796537"} +{"geo_location":{"latitude":48.17129987346022,"longitude":11.563195516162514},"datetime":"1581633802559"} +{"geo_location":{"latitude":48.17130405252229,"longitude":11.563196101547113},"datetime":"1581633808550"} +{"geo_location":{"latitude":48.171298874794,"longitude":11.563190548943389},"datetime":"1581633814652"} +{"geo_location":{"latitude":48.171317882076316,"longitude":11.563186840799217},"datetime":"1581633820551"} +{"geo_location":{"latitude":48.171309370106414,"longitude":11.563195014202554},"datetime":"1581633826557"} +{"geo_location":{"latitude":48.17130828020122,"longitude":11.563180397841093},"datetime":"1581633832559"} +{"geo_location":{"latitude":48.17131014995013,"longitude":11.563192693335825},"datetime":"1581633838542"} +{"geo_location":{"latitude":48.171304562802966,"longitude":11.563215941762108},"datetime":"1581633844548"} diff --git a/packages/pedometer/example/.flutter-plugins-dependencies b/packages/pedometer/example/.flutter-plugins-dependencies index 4970aca48..522387c06 100644 --- a/packages/pedometer/example/.flutter-plugins-dependencies +++ b/packages/pedometer/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"pedometer","path":"/Users/bardram/dev/flutter-plugins/packages/pedometer/","native_build":true,"dependencies":[]}],"android":[{"name":"pedometer","path":"/Users/bardram/dev/flutter-plugins/packages/pedometer/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"pedometer","dependencies":[]}],"date_created":"2023-06-20 19:23:27.397729","version":"3.10.5"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"pedometer","path":"/Users/bardram/dev/flutter-plugins/packages/pedometer/","native_build":true,"dependencies":[]}],"android":[{"name":"pedometer","path":"/Users/bardram/dev/flutter-plugins/packages/pedometer/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"pedometer","dependencies":[]}],"date_created":"2024-06-03 22:02:26.094894","version":"3.19.6"} \ No newline at end of file diff --git a/packages/pedometer/example/ios/Flutter/AppFrameworkInfo.plist b/packages/pedometer/example/ios/Flutter/AppFrameworkInfo.plist index a3e20815c..fa91fa9f5 100644 --- a/packages/pedometer/example/ios/Flutter/AppFrameworkInfo.plist +++ b/packages/pedometer/example/ios/Flutter/AppFrameworkInfo.plist @@ -23,4 +23,4 @@ MinimumOSVersion 9.0 - \ No newline at end of file + diff --git a/packages/pedometer/example/ios/Flutter/Flutter.podspec b/packages/pedometer/example/ios/Flutter/Flutter.podspec index 2c4421cfe..98e163395 100644 --- a/packages/pedometer/example/ios/Flutter/Flutter.podspec +++ b/packages/pedometer/example/ios/Flutter/Flutter.podspec @@ -1,17 +1,17 @@ # -# NOTE: This podspec is NOT to be published. It is only used as a local source! -# This is a generated file; do not edit or check into version control. +# This podspec is NOT to be published. It is only used as a local source! +# This is a generated file; do not edit or check into version control. # Pod::Spec.new do |s| s.name = 'Flutter' s.version = '1.0.0' - s.summary = 'High-performance, high-fidelity mobile apps.' - s.homepage = 'https://flutter.io' - s.license = { :type => 'MIT' } + s.summary = 'A UI toolkit for beautiful and fast apps.' + s.homepage = 'https://flutter.dev' + s.license = { :type => 'BSD' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } - s.ios.deployment_target = '8.0' + s.ios.deployment_target = '12.0' # Framework linking is handled by Flutter tooling, not CocoaPods. # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. s.vendored_frameworks = 'path/to/nothing' diff --git a/packages/pedometer/example/ios/Flutter/flutter_export_environment.sh b/packages/pedometer/example/ios/Flutter/flutter_export_environment.sh index be27e1299..5d5db89f8 100755 --- a/packages/pedometer/example/ios/Flutter/flutter_export_environment.sh +++ b/packages/pedometer/example/ios/Flutter/flutter_export_environment.sh @@ -3,11 +3,12 @@ export "FLUTTER_ROOT=/Users/bardram/dev/flutter" export "FLUTTER_APPLICATION_PATH=/Users/bardram/dev/flutter-plugins/packages/pedometer/example" export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=lib/main.dart" +export "FLUTTER_TARGET=/Users/bardram/dev/flutter-plugins/packages/pedometer/example/lib/main.dart" export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_NAME=1.0.0" export "FLUTTER_BUILD_NUMBER=1" +export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==,RkxVVFRFUl9XRUJfQ0FOVkFTS0lUX1VSTD1odHRwczovL3d3dy5nc3RhdGljLmNvbS9mbHV0dGVyLWNhbnZhc2tpdC9jNGNkNDhlMTg2NDYwYjMyZDQ0NTg1Y2UzYzEwMzI3MWFiNjc2MzU1Lw==" export "DART_OBFUSCATION=false" export "TRACK_WIDGET_CREATION=true" export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=.dart_tool/package_config.json" +export "PACKAGE_CONFIG=/Users/bardram/dev/flutter-plugins/packages/pedometer/example/.dart_tool/package_config.json" diff --git a/packages/pedometer/example/ios/Podfile b/packages/pedometer/example/ios/Podfile index 252d9ec7c..2c068c404 100644 --- a/packages/pedometer/example/ios/Podfile +++ b/packages/pedometer/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '9.0' +platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/packages/pedometer/example/ios/Runner.xcodeproj/project.pbxproj b/packages/pedometer/example/ios/Runner.xcodeproj/project.pbxproj index 79a9a9c9c..0dd899a26 100644 --- a/packages/pedometer/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/pedometer/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -163,7 +163,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -224,10 +224,12 @@ }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -238,6 +240,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -347,7 +350,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -363,14 +366,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = N6R2LB9629; + DEVELOPMENT_TEAM = 59TCTNUBMQ; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -434,7 +437,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -483,7 +486,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -501,14 +504,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = N6R2LB9629; + DEVELOPMENT_TEAM = 59TCTNUBMQ; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -533,14 +536,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = N6R2LB9629; + DEVELOPMENT_TEAM = 59TCTNUBMQ; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/packages/pedometer/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/pedometer/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140cfd..e67b2808a 100644 --- a/packages/pedometer/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/pedometer/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + From e6b493e1d135a821a6be1de8661815c2657a2a0e Mon Sep 17 00:00:00 2001 From: bardram Date: Wed, 31 Jul 2024 13:15:16 +0200 Subject: [PATCH 16/80] pedometer 4.0.2 published --- packages/pedometer/CHANGELOG.md | 4 ++-- packages/pedometer/example/.flutter-plugins-dependencies | 2 +- .../example/ios/Flutter/flutter_export_environment.sh | 5 ++--- packages/pedometer/example/pubspec.yaml | 2 +- packages/pedometer/pubspec.yaml | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/pedometer/CHANGELOG.md b/packages/pedometer/CHANGELOG.md index e80a3d4ac..529cec1f2 100644 --- a/packages/pedometer/CHANGELOG.md +++ b/packages/pedometer/CHANGELOG.md @@ -1,9 +1,9 @@ -## 4.0.1 +## 4.0.2 - Updates Kotlin plugin and AGP. - Upgrade of `compileSdkVersion` to 33. - Upgrade to Dart 3. -- Small updates to example app +- Small updates to example app (asking for permissions) ## 3.0.0 diff --git a/packages/pedometer/example/.flutter-plugins-dependencies b/packages/pedometer/example/.flutter-plugins-dependencies index 522387c06..a50559c92 100644 --- a/packages/pedometer/example/.flutter-plugins-dependencies +++ b/packages/pedometer/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"pedometer","path":"/Users/bardram/dev/flutter-plugins/packages/pedometer/","native_build":true,"dependencies":[]}],"android":[{"name":"pedometer","path":"/Users/bardram/dev/flutter-plugins/packages/pedometer/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"pedometer","dependencies":[]}],"date_created":"2024-06-03 22:02:26.094894","version":"3.19.6"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"pedometer","path":"/Users/bardram/dev/flutter-plugins/packages/pedometer/","native_build":true,"dependencies":[]},{"name":"permission_handler_apple","path":"/Users/bardram/.pub-cache/hosted/pub.dev/permission_handler_apple-9.4.5/","native_build":true,"dependencies":[]}],"android":[{"name":"pedometer","path":"/Users/bardram/dev/flutter-plugins/packages/pedometer/","native_build":true,"dependencies":[]},{"name":"permission_handler_android","path":"/Users/bardram/.pub-cache/hosted/pub.dev/permission_handler_android-12.0.7/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[{"name":"permission_handler_windows","path":"/Users/bardram/.pub-cache/hosted/pub.dev/permission_handler_windows-0.2.1/","native_build":true,"dependencies":[]}],"web":[{"name":"permission_handler_html","path":"/Users/bardram/.pub-cache/hosted/pub.dev/permission_handler_html-0.1.2/","dependencies":[]}]},"dependencyGraph":[{"name":"pedometer","dependencies":[]},{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_html","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_html","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]}],"date_created":"2024-07-31 13:12:00.381228","version":"3.22.2"} \ No newline at end of file diff --git a/packages/pedometer/example/ios/Flutter/flutter_export_environment.sh b/packages/pedometer/example/ios/Flutter/flutter_export_environment.sh index 5d5db89f8..be27e1299 100755 --- a/packages/pedometer/example/ios/Flutter/flutter_export_environment.sh +++ b/packages/pedometer/example/ios/Flutter/flutter_export_environment.sh @@ -3,12 +3,11 @@ export "FLUTTER_ROOT=/Users/bardram/dev/flutter" export "FLUTTER_APPLICATION_PATH=/Users/bardram/dev/flutter-plugins/packages/pedometer/example" export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=/Users/bardram/dev/flutter-plugins/packages/pedometer/example/lib/main.dart" +export "FLUTTER_TARGET=lib/main.dart" export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_NAME=1.0.0" export "FLUTTER_BUILD_NUMBER=1" -export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==,RkxVVFRFUl9XRUJfQ0FOVkFTS0lUX1VSTD1odHRwczovL3d3dy5nc3RhdGljLmNvbS9mbHV0dGVyLWNhbnZhc2tpdC9jNGNkNDhlMTg2NDYwYjMyZDQ0NTg1Y2UzYzEwMzI3MWFiNjc2MzU1Lw==" export "DART_OBFUSCATION=false" export "TRACK_WIDGET_CREATION=true" export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=/Users/bardram/dev/flutter-plugins/packages/pedometer/example/.dart_tool/package_config.json" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/packages/pedometer/example/pubspec.yaml b/packages/pedometer/example/pubspec.yaml index 725f653f4..06bd8a078 100644 --- a/packages/pedometer/example/pubspec.yaml +++ b/packages/pedometer/example/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: flutter: sdk: flutter + permission_handler: ^11.3.0 pedometer: # When depending on this package from a real application you should use: # pedometer: ^x.y.z @@ -17,7 +18,6 @@ dependencies: # the parent directory to use the current plugin's version. path: ../ - cupertino_icons: ^1.0.2 dev_dependencies: flutter_test: diff --git a/packages/pedometer/pubspec.yaml b/packages/pedometer/pubspec.yaml index f676ff350..cca1a29e0 100644 --- a/packages/pedometer/pubspec.yaml +++ b/packages/pedometer/pubspec.yaml @@ -1,6 +1,6 @@ name: pedometer description: A Pedometer and Step Detection package for Android and iOS. Step count is streamed as the platform updates it. -version: 4.0.1 +version: 4.0.2 homepage: https://github.com/cph-cachet/flutter-plugins environment: From 09d18026967e7377742b57e2b97864f5bd0aee4f Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Tue, 6 Aug 2024 10:52:04 +0200 Subject: [PATCH 17/80] [Health] Add menstruation flow data from Health Connect and HealthKit, characteristic data from HealthKit (#1008) * Add characteristic data from HealthKit * Add menstruation data from HealthKit and Google Fit * Fix serialization for characteristic types, fetch new types in example app * Update documentation * Menstruation flow data: remove Google Fit support, add HC support, support writing data on Android * Add support for writing menstruation flow on iOS, read the metadata provided by iOS * Update readme with menstruation flow * Update a doc comment * Added testing of Menstruation Flow to example app --------- Co-authored-by: Aviad Katani Co-authored-by: AviadKa <105856296+AviadKa@users.noreply.github.com> Co-authored-by: bardram --- packages/health/README.md | 6 +- .../cachet/plugins/health/HealthPlugin.kt | 33 +- .../android/app/src/main/AndroidManifest.xml | 2 + packages/health/example/lib/main.dart | 28 +- packages/health/example/lib/util.dart | 5 + .../ios/Classes/SwiftHealthPlugin.swift | 442 ++++++++++++------ packages/health/lib/health.g.dart | 40 ++ .../health/lib/src/health_data_point.dart | 2 + packages/health/lib/src/health_plugin.dart | 45 +- .../health/lib/src/health_value_types.dart | 131 ++++++ packages/health/lib/src/heath_data_types.dart | 15 + 11 files changed, 596 insertions(+), 153 deletions(-) diff --git a/packages/health/README.md b/packages/health/README.md index 8af88edeb..261108f3a 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -348,7 +348,11 @@ The plugin supports the following [`HealthDataType`](https://pub.dev/documentati | AUDIOGRAM | DECIBEL_HEARING_LEVEL | yes | | | | | ELECTROCARDIOGRAM | VOLT | yes | | | Requires Apple Watch to write the data | | NUTRITION | NO_UNIT | yes | yes | yes | | -| INSULIN_DELIVERY | INTERNATIONAL_UNIT | yes | | | | +| INSULIN_DELIVERY | INTERNATIONAL_UNIT | yes | | | | +| GENDER | NO_UNIT | yes | | | | +| BLOOD_TYPE | NO_UNIT | yes | | | | +| BIRTH_DATE | NO_UNIT | yes | | | | +| MENSTRUATION_FLOW | NO_UNIT | yes | | yes | | ## Workout Types diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index f8f772608..181ac60f0 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -96,6 +96,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : private var BASAL_ENERGY_BURNED = "BASAL_ENERGY_BURNED" private var FLIGHTS_CLIMBED = "FLIGHTS_CLIMBED" private var RESPIRATORY_RATE = "RESPIRATORY_RATE" + private var MENSTRUATION_FLOW = "MENSTRUATION_FLOW" // TODO support unknown? private var SLEEP_ASLEEP = "SLEEP_ASLEEP" @@ -1111,6 +1112,16 @@ class HealthPlugin(private var channel: MethodChannel? = null) : } } + /** + * Save menstrual flow data + */ + private fun writeMenstruationFlow(call: MethodCall, result: Result) { + if (useHealthConnectIfAvailable && healthConnectAvailable) { + writeHCData(call, result) + return + } + } + /** * Save the blood oxygen saturation, in Google Fit with the supplemental flow rate, in * HealthConnect without @@ -2507,6 +2518,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "writeWorkoutData" -> writeWorkoutData(call, result) "writeBloodPressure" -> writeBloodPressure(call, result) "writeBloodOxygen" -> writeBloodOxygen(call, result) + "writeMenstruationFlow" -> writeMenstruationFlow(call, result) "writeMeal" -> writeMeal(call, result) "disconnect" -> disconnect(call, result) else -> result.notImplemented() @@ -3464,6 +3476,18 @@ class HealthPlugin(private var channel: MethodChannel? = null) : .packageName, ) ) + is MenstruationFlowRecord -> + return listOf( + mapOf( + "value" to record.flow, + "date_from" to record.time.toEpochMilli(), + "date_to" to record.time.toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ) + ) // is ExerciseSessionRecord -> return listOf(mapOf("value" to , // "date_from" to , // "date_to" to , @@ -3899,6 +3923,11 @@ class HealthPlugin(private var channel: MethodChannel? = null) : startZoneOffset = null, endZoneOffset = null, ) + MENSTRUATION_FLOW -> MenstruationFlowRecord( + time = Instant.ofEpochMilli(startTime), + flow = value.toInt(), + zoneOffset = null, + ) BLOOD_PRESSURE_SYSTOLIC -> throw IllegalArgumentException( "You must use the [writeBloodPressure] API " @@ -4145,7 +4174,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : BASAL_ENERGY_BURNED to BasalMetabolicRateRecord::class, FLIGHTS_CLIMBED to FloorsClimbedRecord::class, RESPIRATORY_RATE to RespiratoryRateRecord::class, - TOTAL_CALORIES_BURNED to TotalCaloriesBurnedRecord::class + TOTAL_CALORIES_BURNED to TotalCaloriesBurnedRecord::class, + MENSTRUATION_FLOW to MenstruationFlowRecord::class, // MOVE_MINUTES to TODO: Find alternative? // TODO: Implement remaining types // "ActiveCaloriesBurned" to @@ -4169,7 +4199,6 @@ class HealthPlugin(private var channel: MethodChannel? = null) : // "Height" to HeightRecord::class, // "Hydration" to HydrationRecord::class, // "LeanBodyMass" to LeanBodyMassRecord::class, - // "MenstruationFlow" to MenstruationFlowRecord::class, // "MenstruationPeriod" to MenstruationPeriodRecord::class, // "Nutrition" to NutritionRecord::class, // "OvulationTest" to OvulationTestRecord::class, diff --git a/packages/health/example/android/app/src/main/AndroidManifest.xml b/packages/health/example/android/app/src/main/AndroidManifest.xml index d92e41cb1..97059243d 100644 --- a/packages/health/example/android/app/src/main/AndroidManifest.xml +++ b/packages/health/example/android/app/src/main/AndroidManifest.xml @@ -60,6 +60,8 @@ + + diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index 4d06749da..bf6138c96 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -58,13 +58,26 @@ class _HealthAppState extends State { // ]; // Set up corresponding permissions + // READ only - List get permissions => - types.map((e) => HealthDataAccess.READ).toList(); + // List get permissions => + // types.map((e) => HealthDataAccess.READ).toList(); // Or both READ and WRITE - // List get permissions => - // types.map((e) => HealthDataAccess.READ_WRITE).toList(); + List get permissions => types + .map((type) => + // can only request READ permissions to the following list of types on iOS + [ + HealthDataType.WALKING_HEART_RATE, + HealthDataType.ELECTROCARDIOGRAM, + HealthDataType.HIGH_HEART_RATE_EVENT, + HealthDataType.LOW_HEART_RATE_EVENT, + HealthDataType.IRREGULAR_HEART_RATE_EVENT, + HealthDataType.EXERCISE_TIME, + ].contains(type) + ? HealthDataAccess.READ + : HealthDataAccess.READ_WRITE) + .toList(); void initState() { // configure the health plugin before use. @@ -326,6 +339,13 @@ class _HealthAppState extends State { // }, // ); + success &= await Health().writeMenstruationFlow( + flow: MenstrualFlow.medium, + isStartOfCycle: true, + startTime: earlier, + endTime: now, + ); + setState(() { _state = success ? AppState.DATA_ADDED : AppState.DATA_NOT_ADDED; }); diff --git a/packages/health/example/lib/util.dart b/packages/health/example/lib/util.dart index 6bfe0c493..093c2c31f 100644 --- a/packages/health/example/lib/util.dart +++ b/packages/health/example/lib/util.dart @@ -53,6 +53,10 @@ const List dataTypesIOS = [ HealthDataType.WALKING_HEART_RATE, HealthDataType.NUTRITION, + HealthDataType.GENDER, + HealthDataType.BLOOD_TYPE, + HealthDataType.BIRTH_DATE, + HealthDataType.MENSTRUATION_FLOW, ]; /// List of data types available on Android. @@ -89,4 +93,5 @@ const List dataTypesAndroid = [ HealthDataType.FLIGHTS_CLIMBED, HealthDataType.NUTRITION, HealthDataType.TOTAL_CALORIES_BURNED, + HealthDataType.MENSTRUATION_FLOW, ]; diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 5c6780539..996c8dc32 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -3,10 +3,11 @@ import HealthKit import UIKit public class SwiftHealthPlugin: NSObject, FlutterPlugin { - + let healthStore = HKHealthStore() var healthDataTypes = [HKSampleType]() var healthDataQuantityTypes = [HKQuantityType]() + var characteristicsDataTypes = [HKCharacteristicType]() var heartRateEventTypes = Set() var headacheType = Set() var allDataTypes = Set() @@ -14,8 +15,9 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { var dataQuantityTypesDict: [String: HKQuantityType] = [:] var unitDict: [String: HKUnit] = [:] var workoutActivityTypeMap: [String: HKWorkoutActivityType] = [:] + var characteristicsTypesDict: [String: HKCharacteristicType] = [:] var nutritionList: [String] = [] - + // Health Data Type Keys let ACTIVE_ENERGY_BURNED = "ACTIVE_ENERGY_BURNED" let AUDIOGRAM = "AUDIOGRAM" @@ -139,7 +141,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let SLEEP_AWAKE = "SLEEP_AWAKE" let SLEEP_DEEP = "SLEEP_DEEP" let SLEEP_REM = "SLEEP_REM" - + let EXERCISE_TIME = "EXERCISE_TIME" let WORKOUT = "WORKOUT" let HEADACHE_UNSPECIFIED = "HEADACHE_UNSPECIFIED" @@ -149,7 +151,12 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let HEADACHE_SEVERE = "HEADACHE_SEVERE" let ELECTROCARDIOGRAM = "ELECTROCARDIOGRAM" let NUTRITION = "NUTRITION" - + let BIRTH_DATE = "BIRTH_DATE" + let GENDER = "GENDER" + let BLOOD_TYPE = "BLOOD_TYPE" + let MENSTRUATION_FLOW = "MENSTRUATION_FLOW" + + // Health Unit types // MOLE_UNIT_WITH_MOLAR_MASS, // requires molar mass input - not supported yet // MOLE_UNIT_WITH_PREFIX_MOLAR_MASS, // requires molar mass & prefix input - not supported yet @@ -201,22 +208,22 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let MILLIGRAM_PER_DECILITER = "MILLIGRAM_PER_DECILITER" let UNKNOWN_UNIT = "UNKNOWN_UNIT" let NO_UNIT = "NO_UNIT" - + struct PluginError: Error { let message: String } - + public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel( name: "flutter_health", binaryMessenger: registrar.messenger()) let instance = SwiftHealthPlugin() registrar.addMethodCallDelegate(instance, channel: channel) } - + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { // Set up all data types initializeTypes() - + /// Handle checkIfHealthDataAvailable if call.method.elementsEqual("checkIfHealthDataAvailable") { checkIfHealthDataAvailable(call: call, result: result) @@ -224,42 +231,42 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else if call.method.elementsEqual("requestAuthorization") { try! requestAuthorization(call: call, result: result) } - + /// Handle getData else if call.method.elementsEqual("getData") { getData(call: call, result: result) } - + /// Handle getIntervalData else if (call.method.elementsEqual("getIntervalData")){ getIntervalData(call: call, result: result) } - + /// Handle getTotalStepsInInterval else if call.method.elementsEqual("getTotalStepsInInterval") { getTotalStepsInInterval(call: call, result: result) } - + /// Handle writeData else if call.method.elementsEqual("writeData") { try! writeData(call: call, result: result) } - + /// Handle writeAudiogram else if call.method.elementsEqual("writeAudiogram") { try! writeAudiogram(call: call, result: result) } - + /// Handle writeBloodPressure else if call.method.elementsEqual("writeBloodPressure") { try! writeBloodPressure(call: call, result: result) } - + /// Handle writeMeal else if (call.method.elementsEqual("writeMeal")){ try! writeMeal(call: call, result: result) } - + /// Handle writeInsulinDelivery else if (call.method.elementsEqual("writeInsulinDelivery")){ try! writeInsulinDelivery(call: call, result: result) @@ -269,29 +276,34 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else if call.method.elementsEqual("writeWorkoutData") { try! writeWorkoutData(call: call, result: result) } - + + /// Handle writeMenstruationFlow + else if call.method.elementsEqual("writeMenstruationFlow") { + try! writeMenstruationFlow(call: call, result: result) + } + /// Handle hasPermission else if call.method.elementsEqual("hasPermissions") { try! hasPermissions(call: call, result: result) } - + /// Handle delete data else if call.method.elementsEqual("delete") { try! delete(call: call, result: result) } - + /// Disconnect else if (call.method.elementsEqual("disconnect")){ // Do nothing. result(true) } - + } - + func checkIfHealthDataAvailable(call: FlutterMethodCall, result: @escaping FlutterResult) { result(HKHealthStore.isHealthDataAvailable()) } - + func hasPermissions(call: FlutterMethodCall, result: @escaping FlutterResult) throws { let arguments = call.arguments as? NSDictionary guard var types = arguments?["types"] as? [String], @@ -300,7 +312,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments!") } - + if let nutritionIndex = types.firstIndex(of: NUTRITION) { types.remove(at: nutritionIndex) let nutritionPermission = permissions[nutritionIndex] @@ -311,7 +323,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { permissions.append(nutritionPermission) } } - + for (index, type) in types.enumerated() { let sampleType = dataTypeLookUp(key: type) let success = hasPermission(type: sampleType, access: permissions[index]) @@ -319,13 +331,20 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { result(success) return } + if let characteristicType = characteristicsTypesDict[type] { + let characteristicSuccess = hasPermission(type: characteristicType, access: permissions[index]) + if (characteristicSuccess == nil || characteristicSuccess == false) { + result(characteristicSuccess) + return + } + } } - - result(true) + + result(false) } - - func hasPermission(type: HKSampleType, access: Int) -> Bool? { - + + func hasPermission(type: HKObjectType, access: Int) -> Bool? { + if #available(iOS 13.0, *) { let status = healthStore.authorizationStatus(for: type) switch access { @@ -340,7 +359,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { return nil } } - + func requestAuthorization(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let types = arguments["types"] as? [String], @@ -349,8 +368,8 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments!") } - - var typesToRead = Set() + + var typesToRead = Set() var typesToWrite = Set() for (index, key) in types.enumerated() { if (key == NUTRITION) { @@ -370,9 +389,20 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { typesToRead.insert(dataType) typesToWrite.insert(dataType) } + if let characteristicsType = characteristicsTypesDict[key] { + let access = permissions[index] + switch access { + case 0: + typesToRead.insert(characteristicsType) + case 1: + throw PluginError(message: "Can not ask for reading permissions to the type of \(characteristicsType)") + default: + break + } + } } } - + if #available(iOS 13.0, *) { healthStore.requestAuthorization(toShare: typesToWrite, read: typesToRead) { (success, error) in @@ -384,7 +414,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { result(false) // Handle the error here. } } - + func writeData(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let value = (arguments["value"] as? Double), @@ -395,12 +425,12 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments") } - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let sample: HKObject - + if dataTypeLookUp(key: type).isKind(of: HKCategoryType.self) { sample = HKCategorySample( type: dataTypeLookUp(key: type) as! HKCategoryType, value: Int(value), start: dateFrom, @@ -411,7 +441,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { type: dataTypeLookUp(key: type) as! HKQuantityType, quantity: quantity, start: dateFrom, end: dateTo) } - + HKHealthStore().save( sample, withCompletion: { (success, error) in @@ -423,7 +453,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func writeAudiogram(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let frequencies = (arguments["frequencies"] as? [Double]), @@ -434,12 +464,12 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments") } - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + var sensitivityPoints = [HKAudiogramSensitivityPoint]() - + for index in 0...frequencies.count - 1 { let frequency = HKQuantity(unit: HKUnit.hertz(), doubleValue: frequencies[index]) let dbUnit = HKUnit.decibelHearingLevel() @@ -449,23 +479,23 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { frequency: frequency, leftEarSensitivity: left, rightEarSensitivity: right) sensitivityPoints.append(sensitivityPoint) } - + let audiogram: HKAudiogramSample let metadataReceived = (arguments["metadata"] as? [String: Any]?) - + if (metadataReceived) != nil { guard let deviceName = metadataReceived?!["HKDeviceName"] as? String else { return } guard let externalUUID = metadataReceived?!["HKExternalUUID"] as? String else { return } - + audiogram = HKAudiogramSample( sensitivityPoints: sensitivityPoints, start: dateFrom, end: dateTo, metadata: [HKMetadataKeyDeviceName: deviceName, HKMetadataKeyExternalUUID: externalUUID]) - + } else { audiogram = HKAudiogramSample( sensitivityPoints: sensitivityPoints, start: dateFrom, end: dateTo, metadata: nil) } - + HKHealthStore().save( audiogram, withCompletion: { (success, error) in @@ -477,7 +507,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func writeBloodPressure(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let systolic = (arguments["systolic"] as? Double), @@ -489,7 +519,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let systolic_sample = HKQuantitySample( type: HKSampleType.quantityType(forIdentifier: .bloodPressureSystolic)!, quantity: HKQuantity(unit: HKUnit.millimeterOfMercury(), doubleValue: systolic), @@ -501,7 +531,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let bpCorrelationType = HKCorrelationType.correlationType(forIdentifier: .bloodPressure)! let bpCorrelation = Set(arrayLiteral: systolic_sample, diastolic_sample) let blood_pressure_sample = HKCorrelation(type: bpCorrelationType , start: dateFrom, end: dateTo, objects: bpCorrelation) - + HKHealthStore().save( [blood_pressure_sample], withCompletion: { (success, error) in @@ -513,27 +543,27 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func writeMeal(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, - let name = (arguments["name"] as? String?), - let startTime = (arguments["start_time"] as? NSNumber), - let endTime = (arguments["end_time"] as? NSNumber), - let mealType = (arguments["meal_type"] as? String?) + let name = (arguments["name"] as? String?), + let startTime = (arguments["start_time"] as? NSNumber), + let endTime = (arguments["end_time"] as? NSNumber), + let mealType = (arguments["meal_type"] as? String?) else { throw PluginError(message: "Invalid Arguments") } let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let mealTypeString = mealType ?? "UNKNOWN" var metadata = ["HKFoodMeal": "\(mealTypeString)"] - + if(name != nil) { metadata[HKMetadataKeyFoodType] = "\(name!)" } - + var nutrition = Set() for (key, identifier) in NUTRITION_KEYS { let value = arguments[key] as? Double @@ -543,11 +573,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { type: HKSampleType.quantityType(forIdentifier: identifier)!, quantity: HKQuantity(unit: unit, doubleValue: unwrappedValue), start: dateFrom, end: dateTo, metadata: metadata) nutrition.insert(nutritionSample) } - + if #available(iOS 15.0, *){ let type = HKCorrelationType.correlationType(forIdentifier: HKCorrelationTypeIdentifier.food)! let meal = HKCorrelation(type: type, start: dateFrom, end: dateTo, objects: nutrition, metadata: metadata) - + HKHealthStore().save(meal, withCompletion: { (success, error) in if let err = error { print("Error Saving Meal Sample: \(err.localizedDescription)") @@ -562,20 +592,20 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } func writeInsulinDelivery(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, - let units = (arguments["units"] as? Double), - let reason = (arguments["reason"] as? NSNumber), - let startTime = (arguments["startTime"] as? NSNumber), - let endTime = (arguments["endTime"] as? NSNumber) + let units = (arguments["units"] as? Double), + let reason = (arguments["reason"] as? NSNumber), + let startTime = (arguments["startTime"] as? NSNumber), + let endTime = (arguments["endTime"] as? NSNumber) else { throw PluginError(message: "Invalid Arguments") } let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let type = HKSampleType.quantityType(forIdentifier: .insulinDelivery)! let quantity = HKQuantity(unit: HKUnit.internationalUnit(), doubleValue: units) let metadata = [HKMetadataKeyInsulinDeliveryReason: reason] - + let insulin_sample = HKQuantitySample(type: type, quantity: quantity, start: dateFrom, end: dateTo, metadata: metadata) HKHealthStore().save(insulin_sample, withCompletion: { (success, error) in @@ -588,6 +618,46 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { }) } + func writeMenstruationFlow(call: FlutterMethodCall, result: @escaping FlutterResult) throws { + guard let arguments = call.arguments as? NSDictionary, + let flow = (arguments["value"] as? Int), + let endTime = (arguments["endTime"] as? NSNumber), + let isStartOfCycle = (arguments["isStartOfCycle"] as? NSNumber) + else { + throw PluginError(message: "Invalid Arguments - value, startTime, endTime or isStartOfCycle invalid") + } + guard let menstrualFlowType = HKCategoryValueMenstrualFlow(rawValue: flow) else { + throw PluginError(message: "Invalid Menstrual Flow Type") + } + + let dateTime = Date(timeIntervalSince1970: endTime.doubleValue / 1000) + + guard let categoryType = HKSampleType.categoryType(forIdentifier: .menstrualFlow) else { + throw PluginError(message: "Invalid Menstrual Flow Type") + } + + let metadata = [HKMetadataKeyMenstrualCycleStart: isStartOfCycle] + + let sample = HKCategorySample( + type: categoryType, + value: menstrualFlowType.rawValue, + start: dateTime, + end: dateTime, + metadata: metadata + ) + + HKHealthStore().save( + sample, + withCompletion: { (success, error) in + if let err = error { + print("Error Saving Menstruation Flow Sample: \(err.localizedDescription)") + } + DispatchQueue.main.async { + result(success) + } + }) + } + func writeWorkoutData(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let activityType = (arguments["activityType"] as? String), @@ -597,10 +667,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments - activityType, startTime or endTime invalid") } - + var totalEnergyBurned: HKQuantity? var totalDistance: HKQuantity? = nil - + // Handle optional arguments if let teb = (arguments["totalEnergyBurned"] as? Double) { totalEnergyBurned = HKQuantity( @@ -610,17 +680,17 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { totalDistance = HKQuantity( unit: unitDict[(arguments["totalDistanceUnit"] as! String)]!, doubleValue: td) } - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + var workout: HKWorkout - + workout = HKWorkout( activityType: ac, start: dateFrom, end: dateTo, duration: dateTo.timeIntervalSince(dateFrom), totalEnergyBurned: totalEnergyBurned ?? nil, totalDistance: totalDistance ?? nil, metadata: nil) - + HKHealthStore().save( workout, withCompletion: { (success, error) in @@ -632,33 +702,33 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func delete(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary let dataTypeKey = (arguments?["dataTypeKey"] as? String)! let startTime = (arguments?["startTime"] as? NSNumber) ?? 0 let endTime = (arguments?["endTime"] as? NSNumber) ?? 0 - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let dataType = dataTypeLookUp(key: dataTypeKey) - + let predicate = HKQuery.predicateForSamples( withStart: dateFrom, end: dateTo, options: .strictStartDate) let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false) - + let deleteQuery = HKSampleQuery( sampleType: dataType, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: [sortDescriptor] ) { [self] x, samplesOrNil, error in - + guard let samplesOrNil = samplesOrNil, error == nil else { // Handle the error if necessary print("Error deleting \(dataType)") return } - + // Delete the retrieved objects from the HealthKit store HKHealthStore().delete(samplesOrNil) { (success, error) in if let err = error { @@ -669,10 +739,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } } } - + HKHealthStore().execute(deleteQuery) } - + func getData(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary let dataTypeKey = (arguments?["dataTypeKey"] as? String)! @@ -681,17 +751,64 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let endTime = (arguments?["endTime"] as? NSNumber) ?? 0 let limit = (arguments?["limit"] as? Int) ?? HKObjectQueryNoLimit let includeManualEntry = (arguments?["includeManualEntry"] as? Bool) ?? true - + // Convert dates from milliseconds to Date() let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let dataType = dataTypeLookUp(key: dataTypeKey) var unit: HKUnit? if let dataUnitKey = dataUnitKey { unit = unitDict[dataUnitKey] } - + + let sourceIdForCharacteristic = "com.apple.Health" + let sourceNameForCharacteristic = "Health" + + switch(dataTypeKey) { + case "BIRTH_DATE": + let dateOfBirth = getBirthDate() + result([ + [ + "value": dateOfBirth?.timeIntervalSince1970, + "date_from": Int(dateFrom.timeIntervalSince1970 * 1000), + "date_to": Int(dateTo.timeIntervalSince1970 * 1000), + "source_id": sourceIdForCharacteristic, + "source_name": sourceNameForCharacteristic, + "is_manual_entry": true + ] + ]) + return + case "GENDER": + let gender = getGender() + result([ + [ + "value": gender?.rawValue, + "date_from": Int(dateFrom.timeIntervalSince1970 * 1000), + "date_to": Int(dateTo.timeIntervalSince1970 * 1000), + "source_id": sourceIdForCharacteristic, + "source_name": sourceNameForCharacteristic, + "is_manual_entry": true + ] + ]) + return + case "BLOOD_TYPE": + let bloodType = getBloodType() + result([ + [ + "value": bloodType?.rawValue, + "date_from": Int(dateFrom.timeIntervalSince1970 * 1000), + "date_to": Int(dateTo.timeIntervalSince1970 * 1000), + "source_id": sourceIdForCharacteristic, + "source_name": sourceNameForCharacteristic, + "is_manual_entry": true + ] + ]) + return + default: + break + } + var predicate = HKQuery.predicateForSamples( withStart: dateFrom, end: dateTo, options: .strictStartDate) if (!includeManualEntry) { @@ -699,13 +816,13 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { predicate = NSCompoundPredicate(type: .and, subpredicates: [predicate, manualPredicate]) } let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false) - + let query = HKSampleQuery( sampleType: dataType, predicate: predicate, limit: limit, sortDescriptors: [sortDescriptor] ) { [self] x, samplesOrNil, error in - + switch samplesOrNil { case let (samples as [HKQuantitySample]) as Any: let dictionaries = samples.map { sample -> NSDictionary in @@ -723,9 +840,9 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { DispatchQueue.main.async { result(dictionaries) } - + case var (samplesCategory as [HKCategorySample]) as Any: - + if dataTypeKey == self.SLEEP_IN_BED { samplesCategory = samplesCategory.filter { $0.value == 0 } } @@ -766,6 +883,14 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { samplesCategory = samplesCategory.filter { $0.value == 4 } } let categories = samplesCategory.map { sample -> NSDictionary in + var metadata: [String: Any] = [:] + + if let sampleMetadata = sample.metadata { + for (key, value) in sampleMetadata { + metadata[key] = value + } + } + return [ "uuid": "\(sample.uuid)", "value": sample.value, @@ -773,15 +898,16 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), "source_id": sample.sourceRevision.source.bundleIdentifier, "source_name": sample.sourceRevision.source.name, - "is_manual_entry": sample.metadata?[HKMetadataKeyWasUserEntered] != nil + "is_manual_entry": sample.metadata?[HKMetadataKeyWasUserEntered] != nil, + "metadata": metadata ] } DispatchQueue.main.async { result(categories) } - + case let (samplesWorkout as [HKWorkout]) as Any: - + let dictionaries = samplesWorkout.map { sample -> NSDictionary in return [ "uuid": "\(sample.uuid)", @@ -802,11 +928,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "total_energy_burned": sample.totalEnergyBurned != nil ? Int(sample.totalEnergyBurned!.doubleValue(for: HKUnit.kilocalorie())) : 0 ] } - + DispatchQueue.main.async { result(dictionaries) } - + case let (samplesAudiogram as [HKAudiogramSample]) as Any: let dictionaries = samplesAudiogram.map { sample -> NSDictionary in var frequencies = [Double]() @@ -833,7 +959,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { DispatchQueue.main.async { result(dictionaries) } - + case let (nutritionSample as [HKCorrelation]) as Any: var foods: [[String: Any?]] = [] for food in nutritionSample { @@ -864,11 +990,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { foods.append(sampleDict) } } - + DispatchQueue.main.async { result(foods) } - + default: if #available(iOS 14.0, *), let ecgSamples = samplesOrNil as? [HKElectrocardiogram] { let dictionaries = ecgSamples.map(fetchEcgMeasurements) @@ -883,10 +1009,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } } } - + HKHealthStore().execute(query) } - + @available(iOS 14.0, *) private func fetchEcgMeasurements(_ sample: HKElectrocardiogram) -> NSDictionary { let semaphore = DispatchSemaphore(value: 0) @@ -920,7 +1046,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "source_name": sample.sourceRevision.source.name, ] } - + func getIntervalData(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary let dataTypeKey = (arguments?["dataTypeKey"] as? String) ?? "DEFAULT" @@ -929,24 +1055,24 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let endDate = (arguments?["endTime"] as? NSNumber) ?? 0 let intervalInSecond = (arguments?["interval"] as? Int) ?? 1 let includeManualEntry = (arguments?["includeManualEntry"] as? Bool) ?? true - + // Set interval in seconds. var interval = DateComponents() interval.second = intervalInSecond - + // Convert dates from milliseconds to Date() let dateFrom = Date(timeIntervalSince1970: startDate.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endDate.doubleValue / 1000) - + let quantityType: HKQuantityType! = dataQuantityTypesDict[dataTypeKey] var predicate = HKQuery.predicateForSamples(withStart: dateFrom, end: dateTo, options: []) if (!includeManualEntry) { let manualPredicate = NSPredicate(format: "metadata.%K != YES", HKMetadataKeyWasUserEntered) predicate = NSCompoundPredicate(type: .and, subpredicates: [predicate, manualPredicate]) } - + let query = HKStatisticsCollectionQuery(quantityType: quantityType, quantitySamplePredicate: predicate, options: [.cumulativeSum, .separateBySource], anchorDate: dateFrom, intervalComponents: interval) - + query.initialResultsHandler = { [weak self] _, statisticCollectionOrNil, error in guard let self = self else { @@ -957,7 +1083,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return } - + // Error detected. if let error = error { print("Query error: \(error.localizedDescription)") @@ -966,7 +1092,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return } - + guard let collection = statisticCollectionOrNil as? HKStatisticsCollection else { print("Unexpected result from query") DispatchQueue.main.async { @@ -974,7 +1100,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return } - + var dictionaries = [[String: Any]]() collection.enumerateStatistics(from: dateFrom, to: dateTo) { [weak self] statisticData, _ in @@ -983,7 +1109,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { print("Self is nil during enumeration") return } - + do { if let quantity = statisticData.sumQuantity(), let dataUnitKey = dataUnitKey, @@ -1007,17 +1133,17 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } HKHealthStore().execute(query) } - + func getTotalStepsInInterval(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary let startTime = (arguments?["startTime"] as? NSNumber) ?? 0 let endTime = (arguments?["endTime"] as? NSNumber) ?? 0 let includeManualEntry = (arguments?["includeManualEntry"] as? Bool) ?? true - + // Convert dates from milliseconds to Date() let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let sampleType = HKQuantityType.quantityType(forIdentifier: .stepCount)! var predicate = HKQuery.predicateForSamples( withStart: dateFrom, end: dateTo, options: .strictStartDate) @@ -1025,53 +1151,86 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let manualPredicate = NSPredicate(format: "metadata.%K != YES", HKMetadataKeyWasUserEntered) predicate = NSCompoundPredicate(type: .and, subpredicates: [predicate, manualPredicate]) } - + let query = HKStatisticsQuery( quantityType: sampleType, quantitySamplePredicate: predicate, options: .cumulativeSum ) { query, queryResult, error in - + guard let queryResult = queryResult else { let error = error! as NSError print("Error getting total steps in interval \(error.localizedDescription)") - + DispatchQueue.main.async { result(nil) } return } - + var steps = 0.0 - + if let quantity = queryResult.sumQuantity() { let unit = HKUnit.count() steps = quantity.doubleValue(for: unit) } - + let totalSteps = Int(steps) DispatchQueue.main.async { result(totalSteps) } } - + HKHealthStore().execute(query) } - + func unitLookUp(key: String) -> HKUnit { guard let unit = unitDict[key] else { return HKUnit.count() } return unit } - + func dataTypeLookUp(key: String) -> HKSampleType { guard let dataType_ = dataTypesDict[key] else { return HKSampleType.quantityType(forIdentifier: .bodyMass)! } return dataType_ } - + + func getGender() -> HKBiologicalSex? { + var bioSex:HKBiologicalSex? + do { + bioSex = try healthStore.biologicalSex().biologicalSex + } catch { + bioSex = nil + print("Error retrieving biologicalSex: \(error)") + } + return bioSex + } + + func getBirthDate() -> Date? { + var dob:Date? + do { + dob = try healthStore.dateOfBirthComponents().date + } catch { + dob = nil + print("Error retrieving date of birth: \(error)") + } + return dob + } + + func getBloodType() -> HKBloodType? { + var bloodType:HKBloodType? + do { + bloodType = try healthStore.bloodType().bloodType + } catch { + bloodType = nil + print("Error retrieving blood type: \(error)") + } + return bloodType + } + func initializeTypes() { // Initialize units unitDict[GRAM] = HKUnit.gram() @@ -1120,7 +1279,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { unitDict[MILLIGRAM_PER_DECILITER] = HKUnit.init(from: "mg/dL") unitDict[UNKNOWN_UNIT] = HKUnit.init(from: "") unitDict[NO_UNIT] = HKUnit.init(from: "") - + // Initialize workout types workoutActivityTypeMap["ARCHERY"] = .archery workoutActivityTypeMap["BOWLING"] = .bowling @@ -1227,7 +1386,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataTypesDict[RESPIRATORY_RATE] = HKSampleType.quantityType(forIdentifier: .respiratoryRate)! dataTypesDict[PERIPHERAL_PERFUSION_INDEX] = HKSampleType.quantityType( forIdentifier: .peripheralPerfusionIndex)! - + dataTypesDict[BLOOD_PRESSURE_DIASTOLIC] = HKSampleType.quantityType( forIdentifier: .bloodPressureDiastolic)! dataTypesDict[BLOOD_PRESSURE_SYSTOLIC] = HKSampleType.quantityType( @@ -1277,7 +1436,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataTypesDict[DIETARY_MANGANESE] = HKSampleType.quantityType(forIdentifier: .dietaryManganese)! dataTypesDict[DIETARY_MOLYBDENUM] = HKSampleType.quantityType(forIdentifier: .dietaryMolybdenum)! dataTypesDict[DIETARY_SELENIUM] = HKSampleType.quantityType(forIdentifier: .dietarySelenium)! - + dataTypesDict[ELECTRODERMAL_ACTIVITY] = HKSampleType.quantityType( forIdentifier: .electrodermalActivity)! dataTypesDict[FORCED_EXPIRATORY_VOLUME] = HKSampleType.quantityType( @@ -1309,15 +1468,22 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataTypesDict[SLEEP_AWAKE] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! dataTypesDict[SLEEP_DEEP] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! dataTypesDict[SLEEP_REM] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! - + dataTypesDict[MENSTRUATION_FLOW] = HKSampleType.categoryType(forIdentifier: .menstrualFlow)! + + dataTypesDict[EXERCISE_TIME] = HKSampleType.quantityType(forIdentifier: .appleExerciseTime)! dataTypesDict[WORKOUT] = HKSampleType.workoutType() dataTypesDict[NUTRITION] = HKSampleType.correlationType( forIdentifier: .food)! - + healthDataTypes = Array(dataTypesDict.values) + + characteristicsTypesDict[BIRTH_DATE] = HKObjectType.characteristicType(forIdentifier: .dateOfBirth)! + characteristicsTypesDict[GENDER] = HKObjectType.characteristicType(forIdentifier: .biologicalSex)! + characteristicsTypesDict[BLOOD_TYPE] = HKObjectType.characteristicType(forIdentifier: .bloodType)! + characteristicsDataTypes = Array(characteristicsTypesDict.values) } - + // Set up iOS 11 specific types (ordinary health data quantity types) if #available(iOS 11.0, *) { dataQuantityTypesDict[ACTIVE_ENERGY_BURNED] = HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned)! @@ -1370,7 +1536,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataQuantityTypesDict[DIETARY_MANGANESE] = HKSampleType.quantityType(forIdentifier: .dietaryManganese)! dataQuantityTypesDict[DIETARY_MOLYBDENUM] = HKSampleType.quantityType(forIdentifier: .dietaryMolybdenum)! dataQuantityTypesDict[DIETARY_SELENIUM] = HKSampleType.quantityType(forIdentifier: .dietarySelenium)! - + dataQuantityTypesDict[ELECTRODERMAL_ACTIVITY] = HKQuantityType.quantityType(forIdentifier: .electrodermalActivity)! dataQuantityTypesDict[FORCED_EXPIRATORY_VOLUME] = HKQuantityType.quantityType(forIdentifier: .forcedExpiratoryVolume1)! dataQuantityTypesDict[HEART_RATE] = HKQuantityType.quantityType(forIdentifier: .heartRate)! @@ -1385,10 +1551,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataQuantityTypesDict[DISTANCE_SWIMMING] = HKQuantityType.quantityType(forIdentifier: .distanceSwimming)! dataQuantityTypesDict[DISTANCE_CYCLING] = HKQuantityType.quantityType(forIdentifier: .distanceCycling)! dataQuantityTypesDict[FLIGHTS_CLIMBED] = HKQuantityType.quantityType(forIdentifier: .flightsClimbed)! - + healthDataQuantityTypes = Array(dataQuantityTypesDict.values) } - + // Set up heart rate data types specific to the apple watch, requires iOS 12 if #available(iOS 12.2, *) { dataTypesDict[HIGH_HEART_RATE_EVENT] = HKSampleType.categoryType( @@ -1397,43 +1563,43 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { forIdentifier: .lowHeartRateEvent)! dataTypesDict[IRREGULAR_HEART_RATE_EVENT] = HKSampleType.categoryType( forIdentifier: .irregularHeartRhythmEvent)! - + heartRateEventTypes = Set([ HKSampleType.categoryType(forIdentifier: .highHeartRateEvent)!, HKSampleType.categoryType(forIdentifier: .lowHeartRateEvent)!, HKSampleType.categoryType(forIdentifier: .irregularHeartRhythmEvent)!, ]) } - + if #available(iOS 13.6, *) { dataTypesDict[HEADACHE_UNSPECIFIED] = HKSampleType.categoryType(forIdentifier: .headache)! dataTypesDict[HEADACHE_NOT_PRESENT] = HKSampleType.categoryType(forIdentifier: .headache)! dataTypesDict[HEADACHE_MILD] = HKSampleType.categoryType(forIdentifier: .headache)! dataTypesDict[HEADACHE_MODERATE] = HKSampleType.categoryType(forIdentifier: .headache)! dataTypesDict[HEADACHE_SEVERE] = HKSampleType.categoryType(forIdentifier: .headache)! - + headacheType = Set([ HKSampleType.categoryType(forIdentifier: .headache)! ]) } - + if #available(iOS 14.0, *) { dataTypesDict[ELECTROCARDIOGRAM] = HKSampleType.electrocardiogramType() - + unitDict[VOLT] = HKUnit.volt() unitDict[INCHES_OF_MERCURY] = HKUnit.inchesOfMercury() - + workoutActivityTypeMap["CARDIO_DANCE"] = HKWorkoutActivityType.cardioDance workoutActivityTypeMap["SOCIAL_DANCE"] = HKWorkoutActivityType.socialDance workoutActivityTypeMap["PICKLEBALL"] = HKWorkoutActivityType.pickleball workoutActivityTypeMap["COOLDOWN"] = HKWorkoutActivityType.cooldown } - + // Concatenate heart events, headache and health data types (both may be empty) allDataTypes = Set(heartRateEventTypes + healthDataTypes) allDataTypes = allDataTypes.union(headacheType) } - + func getWorkoutType(type: HKWorkoutActivityType) -> String { switch type { case .americanFootball: diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart index bd80fffcb..78593c449 100644 --- a/packages/health/lib/health.g.dart +++ b/packages/health/lib/health.g.dart @@ -140,6 +140,10 @@ const _$HealthDataTypeEnumMap = { HealthDataType.HEADACHE_SEVERE: 'HEADACHE_SEVERE', HealthDataType.HEADACHE_UNSPECIFIED: 'HEADACHE_UNSPECIFIED', HealthDataType.NUTRITION: 'NUTRITION', + HealthDataType.GENDER: 'GENDER', + HealthDataType.BIRTH_DATE: 'BIRTH_DATE', + HealthDataType.BLOOD_TYPE: 'BLOOD_TYPE', + HealthDataType.MENSTRUATION_FLOW: 'MENSTRUATION_FLOW', HealthDataType.HIGH_HEART_RATE_EVENT: 'HIGH_HEART_RATE_EVENT', HealthDataType.LOW_HEART_RATE_EVENT: 'LOW_HEART_RATE_EVENT', HealthDataType.IRREGULAR_HEART_RATE_EVENT: 'IRREGULAR_HEART_RATE_EVENT', @@ -669,6 +673,42 @@ Map _$NutritionHealthValueToJson( return val; } +MenstruationFlowHealthValue _$MenstruationFlowHealthValueFromJson( + Map json) => + MenstruationFlowHealthValue( + flow: $enumDecodeNullable(_$MenstrualFlowEnumMap, json['flow']), + dateTime: DateTime.parse(json['date_time'] as String), + isStartOfCycle: json['is_start_of_cycle'] as bool?, + wasUserEntered: json['was_user_entered'] as bool?, + )..$type = json['__type'] as String?; + +Map _$MenstruationFlowHealthValueToJson( + MenstruationFlowHealthValue instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('__type', instance.$type); + writeNotNull('flow', _$MenstrualFlowEnumMap[instance.flow]); + writeNotNull('is_start_of_cycle', instance.isStartOfCycle); + writeNotNull('was_user_entered', instance.wasUserEntered); + val['date_time'] = instance.dateTime.toIso8601String(); + return val; +} + +const _$MenstrualFlowEnumMap = { + MenstrualFlow.unspecified: 'unspecified', + MenstrualFlow.none: 'none', + MenstrualFlow.light: 'light', + MenstrualFlow.medium: 'medium', + MenstrualFlow.heavy: 'heavy', + MenstrualFlow.spotting: 'spotting', +}; + WorkoutSummary _$WorkoutSummaryFromJson(Map json) => WorkoutSummary( workoutType: json['workout_type'] as String, diff --git a/packages/health/lib/src/health_data_point.dart b/packages/health/lib/src/health_data_point.dart index 0c971bf1d..417991fb2 100644 --- a/packages/health/lib/src/health_data_point.dart +++ b/packages/health/lib/src/health_data_point.dart @@ -113,6 +113,8 @@ class HealthDataPoint { NutritionHealthValue.fromHealthDataPoint(dataPoint), HealthDataType.INSULIN_DELIVERY => InsulinDeliveryHealthValue.fromHealthDataPoint(dataPoint), + HealthDataType.MENSTRUATION_FLOW => + MenstruationFlowHealthValue.fromHealthDataPoint(dataPoint), _ => NumericHealthValue.fromHealthDataPoint(dataPoint), }; diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index b4228492b..6fe05ef05 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -20,7 +20,7 @@ part of '../health.dart'; /// * Reading total step counts using the [getTotalStepsInInterval] method. /// * Writing different types of specialized health data like the [writeWorkoutData], /// [writeBloodPressure], [writeBloodOxygen], [writeAudiogram], [writeMeal], -/// [writeInsulinDelivery] methods. +/// [writeMenstruationFlow], [writeInsulinDelivery] methods. class Health { static const MethodChannel _channel = MethodChannel('flutter_health'); static final _instance = Health._(); @@ -661,6 +661,40 @@ class Health { return success ?? false; } + /// Save menstruation flow into Apple Health and Google Health Connect. + /// + /// Returns true if successful, false otherwise. + /// + /// Parameters: + /// * [flow] - the menstrual flow + /// * [startTime] - the start time when the menstrual flow is measured. + /// * [endTime] - the start time when the menstrual flow is measured. + /// * [isStartOfCycle] - A bool that indicates whether the sample represents + /// the start of a menstrual cycle. + Future writeMenstruationFlow({ + required MenstrualFlow flow, + required DateTime startTime, + required DateTime endTime, + required bool isStartOfCycle, + }) async { + var value = + Platform.isAndroid ? MenstrualFlow.toHealthConnect(flow) : flow.index; + + if (value == -1) { + throw ArgumentError( + "$flow is not a valid menstrual flow value for $platformType"); + } + + Map args = { + 'value': value, + 'startTime': startTime.millisecondsSinceEpoch, + 'endTime': endTime.millisecondsSinceEpoch, + 'isStartOfCycle': isStartOfCycle, + 'dataTypeKey': HealthDataType.MENSTRUATION_FLOW.name, + }; + return await _channel.invokeMethod('writeMenstruationFlow', args) == true; + } + /// Saves audiogram into Apple Health. Not supported on Android. /// /// Returns true if successful, false otherwise. @@ -988,13 +1022,8 @@ class Health { /// Returns null if not successful. /// /// Is a fix according to https://stackoverflow.com/questions/29414386/step-count-retrieved-through-google-fit-api-does-not-match-step-count-displayed/29415091#29415091 - Future getTotalStepsInInterval( - DateTime startTime, - DateTime endTime, - { - bool includeManualEntry = true - } - ) async { + Future getTotalStepsInInterval(DateTime startTime, DateTime endTime, + {bool includeManualEntry = true}) async { final args = { 'startTime': startTime.millisecondsSinceEpoch, 'endTime': endTime.millisecondsSinceEpoch, diff --git a/packages/health/lib/src/health_value_types.dart b/packages/health/lib/src/health_value_types.dart index 9c42ca6f8..d3d364141 100644 --- a/packages/health/lib/src/health_value_types.dart +++ b/packages/health/lib/src/health_value_types.dart @@ -759,3 +759,134 @@ class NutritionHealthValue extends HealthValue { zinc, ]); } + +enum MenstrualFlow { + unspecified, + none, + light, + medium, + heavy, + spotting; + + static MenstrualFlow fromHealthConnect(int value) { + switch (value) { + case 0: + return MenstrualFlow.unspecified; + case 1: + return MenstrualFlow.light; + case 2: + return MenstrualFlow.medium; + case 3: + return MenstrualFlow.heavy; + default: + return MenstrualFlow.unspecified; + } + } + + static MenstrualFlow fromHealthKit(int value) { + switch (value) { + case 1: + return MenstrualFlow.unspecified; + case 2: + return MenstrualFlow.light; + case 3: + return MenstrualFlow.medium; + case 4: + return MenstrualFlow.heavy; + case 5: + return MenstrualFlow.none; + default: + return MenstrualFlow.unspecified; + } + } + + static int toHealthConnect(MenstrualFlow value) { + switch (value) { + case MenstrualFlow.unspecified: + return 0; + case MenstrualFlow.light: + return 1; + case MenstrualFlow.medium: + return 2; + case MenstrualFlow.heavy: + return 3; + default: + return -1; + } + } +} + +/// A [HealthValue] object for menstrual flow. +/// +/// Parameters: +/// * [flowValue] - the flow value +/// * [isStartOfCycle] - indicator whether or not this occurrence is the first day of the menstrual cycle (iOS only) +/// * [wasUserEntered] - indicator whether or not the data was entered by the user (iOS only) +/// * [dateTime] - the date and time of the menstrual flow +@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) +class MenstruationFlowHealthValue extends HealthValue { + final MenstrualFlow? flow; + final bool? isStartOfCycle; + final bool? wasUserEntered; + final DateTime dateTime; + + MenstruationFlowHealthValue({ + required this.flow, + required this.dateTime, + this.isStartOfCycle, + this.wasUserEntered, + }); + + @override + String toString() => + "flow: ${flow?.name}, startOfCycle: $isStartOfCycle, wasUserEntered: $wasUserEntered, dateTime: $dateTime"; + + factory MenstruationFlowHealthValue.fromHealthDataPoint(dynamic dataPoint) { + // Parse flow value safely + final flowValueIndex = dataPoint['value'] as int? ?? 0; + MenstrualFlow? menstrualFlow; + if (Platform.isAndroid) { + menstrualFlow = MenstrualFlow.fromHealthConnect(flowValueIndex); + } else if (Platform.isIOS) { + menstrualFlow = MenstrualFlow.fromHealthKit(flowValueIndex); + } + + return MenstruationFlowHealthValue( + flow: menstrualFlow, + isStartOfCycle: + dataPoint['metadata']?.containsKey('HKMenstrualCycleStart') == true + ? dataPoint['metadata']['HKMenstrualCycleStart'] == 1.0 + : null, + wasUserEntered: + dataPoint['metadata']?.containsKey('HKWasUserEntered') == true + ? dataPoint['metadata']['HKWasUserEntered'] == 1.0 + : null, + dateTime: + DateTime.fromMillisecondsSinceEpoch(dataPoint['date_from'] as int), + ); + } + + @override + Function get fromJsonFunction => _$MenstruationFlowHealthValueFromJson; + + factory MenstruationFlowHealthValue.fromJson(Map json) => + FromJsonFactory().fromJson(json) as MenstruationFlowHealthValue; + + @override + Map toJson() => _$MenstruationFlowHealthValueToJson(this); + + @override + bool operator ==(Object other) { + return identical(this, other) || + other is MenstruationFlowHealthValue && + runtimeType == other.runtimeType && + flow == other.flow && + isStartOfCycle == other.isStartOfCycle && + wasUserEntered == other.wasUserEntered && + dateTime == other.dateTime; + } + + @override + int get hashCode => + Object.hash(flow, isStartOfCycle, wasUserEntered, dateTime); +} diff --git a/packages/health/lib/src/heath_data_types.dart b/packages/health/lib/src/heath_data_types.dart index c8994cae2..8730c7771 100644 --- a/packages/health/lib/src/heath_data_types.dart +++ b/packages/health/lib/src/heath_data_types.dart @@ -90,6 +90,11 @@ enum HealthDataType { HEADACHE_SEVERE, HEADACHE_UNSPECIFIED, NUTRITION, + // HealthKit Characteristics + GENDER, + BIRTH_DATE, + BLOOD_TYPE, + MENSTRUATION_FLOW, // Heart Rate events (specific to Apple Watch) HIGH_HEART_RATE_EVENT, @@ -198,6 +203,10 @@ const List dataTypeKeysIOS = [ HealthDataType.HEADACHE_UNSPECIFIED, HealthDataType.ELECTROCARDIOGRAM, HealthDataType.NUTRITION, + HealthDataType.GENDER, + HealthDataType.BIRTH_DATE, + HealthDataType.BLOOD_TYPE, + HealthDataType.MENSTRUATION_FLOW, ]; /// List of data types available on Android @@ -233,6 +242,7 @@ const List dataTypeKeysAndroid = [ HealthDataType.RESPIRATORY_RATE, HealthDataType.NUTRITION, HealthDataType.TOTAL_CALORIES_BURNED, + HealthDataType.MENSTRUATION_FLOW, ]; /// Maps a [HealthDataType] to a [HealthDataUnit]. @@ -329,6 +339,10 @@ const Map dataTypeToUnit = { HealthDataType.HEADACHE_SEVERE: HealthDataUnit.MINUTE, HealthDataType.HEADACHE_UNSPECIFIED: HealthDataUnit.MINUTE, + HealthDataType.GENDER: HealthDataUnit.NO_UNIT, + HealthDataType.BIRTH_DATE: HealthDataUnit.NO_UNIT, + HealthDataType.BLOOD_TYPE: HealthDataUnit.NO_UNIT, + // Heart Rate events (specific to Apple Watch) HealthDataType.HIGH_HEART_RATE_EVENT: HealthDataUnit.NO_UNIT, HealthDataType.LOW_HEART_RATE_EVENT: HealthDataUnit.NO_UNIT, @@ -337,6 +351,7 @@ const Map dataTypeToUnit = { HealthDataType.ELECTROCARDIOGRAM: HealthDataUnit.VOLT, HealthDataType.NUTRITION: HealthDataUnit.NO_UNIT, + HealthDataType.MENSTRUATION_FLOW: HealthDataUnit.NO_UNIT, // Health Connect HealthDataType.TOTAL_CALORIES_BURNED: HealthDataUnit.KILOCALORIE, From 443398141ff121a4643093232a2d664b5bef1b36 Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Tue, 6 Aug 2024 13:47:40 +0200 Subject: [PATCH 18/80] [Health] Add heart rate variability support for Health Connect (#1009) * Add support for heart rate variability (RMSSD) for Android * Update readme --- packages/health/README.md | 259 ++++++++++++++---- .../cachet/plugins/health/HealthPlugin.kt | 32 ++- .../android/app/src/main/AndroidManifest.xml | 3 + packages/health/example/lib/main.dart | 5 + packages/health/example/lib/util.dart | 1 + packages/health/lib/health.g.dart | 1 + packages/health/lib/src/heath_data_types.dart | 3 + 7 files changed, 248 insertions(+), 56 deletions(-) diff --git a/packages/health/README.md b/packages/health/README.md index 261108f3a..c908a2ad0 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -192,6 +192,7 @@ Below is a simplified flow of how to use the plugin. // configure the health plugin before use. Health().configure(useHealthConnectIfAvailable: true); + // define the types to get var types = [ HealthDataType.STEPS, @@ -261,6 +262,19 @@ A `HealthDataPoint` object can be serialized to and from JSON using the `toJson( "source_id": "com.apple.health.81AE7156-EC05-47E3-AC93-2D6F65C717DF", "source_name": "iPhone12.bardram.net", "is_manual_entry": false + "value": { + "__type": "NumericHealthValue", + "numeric_value": 141.0 + }, + "type": "STEPS", + "unit": "COUNT", + "date_from": "2024-04-03T10:06:57.736", + "date_to": "2024-04-03T10:12:51.724", + "source_platform": "appleHealth", + "source_device_id": "F74938B9-C011-4DE4-AA5E-CF41B60B96E7", + "source_id": "com.apple.health.81AE7156-EC05-47E3-AC93-2D6F65C717DF", + "source_name": "iPhone12.bardram.net", + "is_manual_entry": false } ``` @@ -298,61 +312,58 @@ points = Health().removeDuplicates(points); The plugin supports the following [`HealthDataType`](https://pub.dev/documentation/health/latest/health/HealthDataType.html). -| **Data Type** | **Unit** | **Apple Health** | **Google Fit** | **Google Health Connect** | **Comments** | -| --------------------------- | ----------------------- | ---------------- | -------------- | ------------------------- | -------------------------------------- | -| ACTIVE_ENERGY_BURNED | CALORIES | yes | yes | yes | | -| BASAL_ENERGY_BURNED | CALORIES | yes | | yes | | -| BLOOD_GLUCOSE | MILLIGRAM_PER_DECILITER | yes | yes | yes | | -| BLOOD_OXYGEN | PERCENTAGE | yes | yes | yes | | -| BLOOD_PRESSURE_DIASTOLIC | MILLIMETER_OF_MERCURY | yes | yes | yes | | -| BLOOD_PRESSURE_SYSTOLIC | MILLIMETER_OF_MERCURY | yes | yes | yes | | -| BODY_FAT_PERCENTAGE | PERCENTAGE | yes | yes | yes | | -| BODY_MASS_INDEX | NO_UNIT | yes | yes | yes | | -| BODY_TEMPERATURE | DEGREE_CELSIUS | yes | yes | yes | | -| BODY_WATER_MASS | KILOGRAMS | | | yes | | -| ELECTRODERMAL_ACTIVITY | SIEMENS | yes | | | | -| HEART_RATE | BEATS_PER_MINUTE | yes | yes | yes | | -| HEIGHT | METERS | yes | yes | yes | | -| RESTING_HEART_RATE | BEATS_PER_MINUTE | yes | | yes | | -| RESPIRATORY_RATE | RESPIRATIONS_PER_MINUTE | yes | | yes | | -| PERIPHERAL_PERFUSION_INDEX | PERCENTAGE | yes | | | | -| STEPS | COUNT | yes | yes | yes | | -| WAIST_CIRCUMFERENCE | METERS | yes | | | | -| WALKING_HEART_RATE | BEATS_PER_MINUTE | yes | | | | -| WEIGHT | KILOGRAMS | yes | yes | yes | | -| DISTANCE_WALKING_RUNNING | METERS | yes | | | | -| FLIGHTS_CLIMBED | COUNT | yes | | yes | | -| MOVE_MINUTES | MINUTES | | yes | | | -| DISTANCE_DELTA | METERS | | yes | yes | | -| MINDFULNESS | MINUTES | yes | | | | -| SLEEP_IN_BED | MINUTES | yes | | | | -| SLEEP_ASLEEP | MINUTES | yes | | yes | | -| SLEEP_AWAKE | MINUTES | yes | | yes | | -| SLEEP_DEEP | MINUTES | yes | | yes | | -| SLEEP_LIGHT | MINUTES | | | yes | | -| SLEEP_REM | MINUTES | yes | | yes | | -| SLEEP_OUT_OF_BED | MINUTES | | | yes | | -| SLEEP_SESSION | MINUTES | | | yes | | -| WATER | LITER | yes | yes | yes | | -| EXERCISE_TIME | MINUTES | yes | | | | -| WORKOUT | NO_UNIT | yes | yes | yes | See table below | -| HIGH_HEART_RATE_EVENT | NO_UNIT | yes | | | Requires Apple Watch to write the data | -| LOW_HEART_RATE_EVENT | NO_UNIT | yes | | | Requires Apple Watch to write the data | -| IRREGULAR_HEART_RATE_EVENT | NO_UNIT | yes | | | Requires Apple Watch to write the data | -| HEART_RATE_VARIABILITY_SDNN | MILLISECONDS | yes | | | Requires Apple Watch to write the data | -| HEADACHE_NOT_PRESENT | MINUTES | yes | | | | -| HEADACHE_MILD | MINUTES | yes | | | | -| HEADACHE_MODERATE | MINUTES | yes | | | | -| HEADACHE_SEVERE | MINUTES | yes | | | | -| HEADACHE_UNSPECIFIED | MINUTES | yes | | | | -| AUDIOGRAM | DECIBEL_HEARING_LEVEL | yes | | | | -| ELECTROCARDIOGRAM | VOLT | yes | | | Requires Apple Watch to write the data | -| NUTRITION | NO_UNIT | yes | yes | yes | | -| INSULIN_DELIVERY | INTERNATIONAL_UNIT | yes | | | | -| GENDER | NO_UNIT | yes | | | | -| BLOOD_TYPE | NO_UNIT | yes | | | | -| BIRTH_DATE | NO_UNIT | yes | | | | -| MENSTRUATION_FLOW | NO_UNIT | yes | | yes | | +| **Data Type** | **Unit** | **Apple Health** | **Google Fit** | **Google Health Connect** | **Comments** | +| ---------------------------- | ----------------------- | ---------------- | -------------- | ------------------------- | -------------------------------------- | +| ACTIVE_ENERGY_BURNED | CALORIES | yes | yes | yes | | +| BASAL_ENERGY_BURNED | CALORIES | yes | | yes | | +| BLOOD_GLUCOSE | MILLIGRAM_PER_DECILITER | yes | yes | yes | | +| BLOOD_OXYGEN | PERCENTAGE | yes | yes | yes | | +| BLOOD_PRESSURE_DIASTOLIC | MILLIMETER_OF_MERCURY | yes | yes | yes | | +| BLOOD_PRESSURE_SYSTOLIC | MILLIMETER_OF_MERCURY | yes | yes | yes | | +| BODY_FAT_PERCENTAGE | PERCENTAGE | yes | yes | yes | | +| BODY_MASS_INDEX | NO_UNIT | yes | yes | yes | | +| BODY_TEMPERATURE | DEGREE_CELSIUS | yes | yes | yes | | +| BODY_WATER_MASS | KILOGRAMS | | | yes | | +| ELECTRODERMAL_ACTIVITY | SIEMENS | yes | | | | +| HEART_RATE | BEATS_PER_MINUTE | yes | yes | yes | | +| HEIGHT | METERS | yes | yes | yes | | +| RESTING_HEART_RATE | BEATS_PER_MINUTE | yes | | yes | | +| RESPIRATORY_RATE | RESPIRATIONS_PER_MINUTE | yes | | yes | | +| PERIPHERAL_PERFUSION_INDEX | PERCENTAGE | yes | | | | +| STEPS | COUNT | yes | yes | yes | | +| WAIST_CIRCUMFERENCE | METERS | yes | | | | +| WALKING_HEART_RATE | BEATS_PER_MINUTE | yes | | | | +| WEIGHT | KILOGRAMS | yes | yes | yes | | +| DISTANCE_WALKING_RUNNING | METERS | yes | | | | +| FLIGHTS_CLIMBED | COUNT | yes | | yes | | +| MOVE_MINUTES | MINUTES | | yes | | | +| DISTANCE_DELTA | METERS | | yes | yes | | +| MINDFULNESS | MINUTES | yes | | | | +| SLEEP_IN_BED | MINUTES | yes | | | | +| SLEEP_ASLEEP | MINUTES | yes | | yes | | +| SLEEP_AWAKE | MINUTES | yes | | yes | | +| SLEEP_DEEP | MINUTES | yes | | yes | | +| SLEEP_LIGHT | MINUTES | | | yes | | +| SLEEP_REM | MINUTES | yes | | yes | | +| SLEEP_OUT_OF_BED | MINUTES | | | yes | | +| SLEEP_SESSION | MINUTES | | | yes | | +| WATER | LITER | yes | yes | yes | | +| EXERCISE_TIME | MINUTES | yes | | | | +| WORKOUT | NO_UNIT | yes | yes | yes | See table below | +| HIGH_HEART_RATE_EVENT | NO_UNIT | yes | | | Requires Apple Watch to write the data | +| LOW_HEART_RATE_EVENT | NO_UNIT | yes | | | Requires Apple Watch to write the data | +| IRREGULAR_HEART_RATE_EVENT | NO_UNIT | yes | | | Requires Apple Watch to write the data | +| HEART_RATE_VARIABILITY_RMSSD | MILLISECONDS | | | yes | | +| HEART_RATE_VARIABILITY_SDNN | MILLISECONDS | yes | | | Requires Apple Watch to write the data | +| HEADACHE_NOT_PRESENT | MINUTES | yes | | | | +| HEADACHE_MILD | MINUTES | yes | | | | +| HEADACHE_MODERATE | MINUTES | yes | | | | +| HEADACHE_SEVERE | MINUTES | yes | | | | +| HEADACHE_UNSPECIFIED | MINUTES | yes | | | | +| AUDIOGRAM | DECIBEL_HEARING_LEVEL | yes | | | | +| ELECTROCARDIOGRAM | VOLT | yes | | | Requires Apple Watch to write the data | +| NUTRITION | NO_UNIT | yes | yes | yes | | +| INSULIN_DELIVERY | INTERNATIONAL_UNIT | yes | | | | ## Workout Types @@ -497,3 +508,141 @@ The plugin supports the following [`HealthWorkoutActivityType`](https://pub.dev/ | WINDSURFING | | yes | | | | ZUMBA | | yes | | | | OTHER | yes | yes | | | +| -------------------------------- | ---------------- | -------------- | ------------------------- | ----------------------------------------------------------------- | +| ARCHERY | yes | yes | | | +| BADMINTON | yes | yes | yes | | +| BASEBALL | yes | yes | yes | | +| BASKETBALL | yes | yes | yes | | +| BIKING | yes | yes | yes | on iOS this is CYCLING, but name changed here to fit with Android | +| BOXING | yes | yes | yes | | +| CRICKET | yes | yes | yes | | +| CURLING | yes | yes | | | +| ELLIPTICAL | yes | yes | yes | | +| FENCING | yes | yes | yes | | +| AMERICAN_FOOTBALL | yes | yes | yes | | +| AUSTRALIAN_FOOTBALL | yes | yes | yes | | +| SOCCER | yes | yes | | | +| GOLF | yes | yes | yes | | +| GYMNASTICS | yes | yes | yes | | +| HANDBALL | yes | yes | yes | | +| HIGH_INTENSITY_INTERVAL_TRAINING | yes | yes | yes | | +| HIKING | yes | yes | yes | | +| HOCKEY | yes | yes | | | +| SKATING | yes | yes | yes | On iOS this is skating_sports | +| JUMP_ROPE | yes | yes | | | +| KICKBOXING | yes | yes | | | +| MARTIAL_ARTS | yes | yes | yes | | +| PILATES | yes | yes | yes | | +| RACQUETBALL | yes | yes | yes | | +| RUGBY | yes | yes | yes | | +| RUNNING | yes | yes | yes | | +| ROWING | yes | yes | yes | | +| SAILING | yes | yes | yes | | +| CROSS_COUNTRY_SKIING | yes | yes | | | +| DOWNHILL_SKIING | yes | yes | | | +| SNOWBOARDING | yes | yes | yes | | +| SOFTBALL | yes | yes | yes | | +| SQUASH | yes | yes | yes | | +| STAIR_CLIMBING | yes | yes | yes | | +| SWIMMING | yes | yes | | | +| TABLE_TENNIS | yes | yes | yes | | +| TENNIS | yes | yes | yes | | +| VOLLEYBALL | yes | yes | yes | | +| WALKING | yes | yes | yes | | +| WATER_POLO | yes | yes | yes | | +| YOGA | yes | yes | yes | | +| BOWLING | yes | | | | +| CROSS_TRAINING | yes | | | | +| TRACK_AND_FIELD | yes | | | | +| DISC_SPORTS | yes | | | | +| LACROSSE | yes | | | | +| PREPARATION_AND_RECOVERY | yes | | | | +| FLEXIBILITY | yes | | | | +| COOLDOWN | yes | | | | +| WHEELCHAIR_WALK_PACE | yes | | | | +| WHEELCHAIR_RUN_PACE | yes | | | | +| HAND_CYCLING | yes | | | | +| CORE_TRAINING | yes | | | | +| FUNCTIONAL_STRENGTH_TRAINING | yes | | | | +| TRADITIONAL_STRENGTH_TRAINING | yes | | | | +| MIXED_CARDIO | yes | | | | +| STAIRS | yes | | | | +| STEP_TRAINING | yes | | | | +| FITNESS_GAMING | yes | | | | +| BARRE | yes | | | | +| CARDIO_DANCE | yes | | | | +| SOCIAL_DANCE | yes | | | | +| MIND_AND_BODY | yes | | | | +| PICKLEBALL | yes | | | | +| CLIMBING | yes | | | | +| EQUESTRIAN_SPORTS | yes | | | | +| FISHING | yes | | | | +| HUNTING | yes | | | | +| PLAY | yes | | | | +| SNOW_SPORTS | yes | | | | +| PADDLE_SPORTS | yes | | | | +| SURFING_SPORTS | yes | | | | +| WATER_FITNESS | yes | | | | +| WATER_SPORTS | yes | | | | +| TAI_CHI | yes | | | | +| WRESTLING | yes | | | | +| AEROBICS | | yes | | | +| BIATHLON | | yes | | | +| CALISTHENICS | | yes | yes | | +| CIRCUIT_TRAINING | | yes | | | +| CROSS_FIT | | yes | | | +| DANCING | | yes | yes | | +| DIVING | | yes | | | +| ELEVATOR | | yes | | | +| ERGOMETER | | yes | | | +| ESCALATOR | | yes | | | +| FRISBEE_DISC | | yes | yes | | +| GARDENING | | yes | | | +| GUIDED_BREATHING | | yes | yes | | +| HORSEBACK_RIDING | | yes | | | +| HOUSEWORK | | yes | | | +| INTERVAL_TRAINING | | yes | | | +| IN_VEHICLE | | yes | | | +| KAYAKING | | yes | | | +| KETTLEBELL_TRAINING | | yes | | | +| KICK_SCOOTER | | yes | | | +| KITE_SURFING | | yes | | | +| MEDITATION | | yes | | | +| MIXED_MARTIAL_ARTS | | yes | | | +| P90X | | yes | | | +| PARAGLIDING | | yes | yes | | +| POLO | | yes | | | +| ROCK_CLIMBING | (yes) | yes | yes | on iOS this will be stored as CLIMBING | +| RUNNING_JOGGING | (yes) | yes | | on iOS this will be stored as RUNNING | +| RUNNING_SAND | (yes) | yes | | on iOS this will be stored as RUNNING | +| RUNNING_TREADMILL | (yes) | yes | yes | on iOS this will be stored as RUNNING | +| SCUBA_DIVING | | yes | yes | | +| SKATING_CROSS | (yes) | yes | | on iOS this will be stored as SKATING | +| SKATING_INDOOR | (yes) | yes | | on iOS this will be stored as SKATING | +| SKATING_INLINE | (yes) | yes | | on iOS this will be stored as SKATING | +| SKIING_BACK_COUNTRY | | yes | | | +| SKIING_KITE | | yes | | | +| SKIING_ROLLER | | yes | | | +| SLEDDING | | yes | | | +| STAIR_CLIMBING_MACHINE | | yes | yes | | +| STANDUP_PADDLEBOARDING | | yes | | | +| STILL | | yes | | | +| STRENGTH_TRAINING | | yes | yes | | +| SURFING | | yes | yes | | +| SWIMMING_OPEN_WATER | | yes | yes | | +| SWIMMING_POOL | | yes | yes | | +| TEAM_SPORTS | | yes | | | +| TILTING | | yes | | | +| TREADMILL | | yes | | | +| VOLLEYBALL_BEACH | | yes | | | +| VOLLEYBALL_INDOOR | | yes | | | +| WAKEBOARDING | | yes | | | +| WALKING_FITNESS | | yes | | | +| WALKING_NORDIC | | yes | | | +| WALKING_STROLLER | | yes | | | +| WALKING_TREADMILL | | yes | | | +| WEIGHTLIFTING | | yes | yes | | +| WHEELCHAIR | | yes | yes | | +| WINDSURFING | | yes | | | +| ZUMBA | | yes | | | +| OTHER | yes | yes | | | diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 181ac60f0..0455c9c6b 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -89,6 +89,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : private var BLOOD_PRESSURE_DIASTOLIC = "BLOOD_PRESSURE_DIASTOLIC" private var BLOOD_OXYGEN = "BLOOD_OXYGEN" private var BLOOD_GLUCOSE = "BLOOD_GLUCOSE" + private var HEART_RATE_VARIABILITY_RMSSD = "HEART_RATE_VARIABILITY_RMSSD" private var MOVE_MINUTES = "MOVE_MINUTES" private var DISTANCE_DELTA = "DISTANCE_DELTA" private var WATER = "WATER" @@ -2527,7 +2528,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : override fun onAttachedToActivity(binding: ActivityPluginBinding) { if (channel == null) { - return + return } binding.addActivityResultListener(this) activity = binding.activity @@ -3158,6 +3159,23 @@ class HealthPlugin(private var channel: MethodChannel? = null) : .packageName, ) } + is HeartRateVariabilityRmssdRecord -> + return listOf( + mapOf( + "value" to + record.heartRateVariabilityMillis, + "date_from" to + record.time + .toEpochMilli(), + "date_to" to + record.time + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) is BodyTemperatureRecord -> return listOf( mapOf( @@ -3651,6 +3669,17 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ), zoneOffset = null, ) + HEART_RATE_VARIABILITY_RMSSD -> + HeartRateVariabilityRmssdRecord( + time = + Instant.ofEpochMilli( + startTime + ), + heartRateVariabilityMillis = + value, + + zoneOffset = null, + ) DISTANCE_DELTA -> DistanceRecord( startTime = @@ -4159,6 +4188,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : BLOOD_PRESSURE_DIASTOLIC to BloodPressureRecord::class, BLOOD_OXYGEN to OxygenSaturationRecord::class, BLOOD_GLUCOSE to BloodGlucoseRecord::class, + HEART_RATE_VARIABILITY_RMSSD to HeartRateVariabilityRmssdRecord::class, DISTANCE_DELTA to DistanceRecord::class, WATER to HydrationRecord::class, SLEEP_ASLEEP to SleepSessionRecord::class, diff --git a/packages/health/example/android/app/src/main/AndroidManifest.xml b/packages/health/example/android/app/src/main/AndroidManifest.xml index 97059243d..f6d185ace 100644 --- a/packages/health/example/android/app/src/main/AndroidManifest.xml +++ b/packages/health/example/android/app/src/main/AndroidManifest.xml @@ -60,6 +60,9 @@ + + + diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index bf6138c96..59eb830c9 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -217,6 +217,11 @@ class _HealthAppState extends State { type: HealthDataType.HEART_RATE, startTime: earlier, endTime: now); + success &= await Health().writeHealthData( + value: 30, + type: HealthDataType.HEART_RATE_VARIABILITY_RMSSD, + startTime: earlier, + endTime: now); success &= await Health().writeHealthData( value: 37, type: HealthDataType.BODY_TEMPERATURE, diff --git a/packages/health/example/lib/util.dart b/packages/health/example/lib/util.dart index 093c2c31f..f1bfcb44f 100644 --- a/packages/health/example/lib/util.dart +++ b/packages/health/example/lib/util.dart @@ -77,6 +77,7 @@ const List dataTypesAndroid = [ // HealthDataType.BODY_MASS_INDEX, HealthDataType.BODY_TEMPERATURE, HealthDataType.HEART_RATE, + HealthDataType.HEART_RATE_VARIABILITY_RMSSD, HealthDataType.STEPS, // HealthDataType.MOVE_MINUTES, // TODO: Find alternative for Health Connect HealthDataType.DISTANCE_DELTA, diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart index 78593c449..fbbfcc13c 100644 --- a/packages/health/lib/health.g.dart +++ b/packages/health/lib/health.g.dart @@ -104,6 +104,7 @@ const _$HealthDataTypeEnumMap = { HealthDataType.FORCED_EXPIRATORY_VOLUME: 'FORCED_EXPIRATORY_VOLUME', HealthDataType.HEART_RATE: 'HEART_RATE', HealthDataType.HEART_RATE_VARIABILITY_SDNN: 'HEART_RATE_VARIABILITY_SDNN', + HealthDataType.HEART_RATE_VARIABILITY_RMSSD: 'HEART_RATE_VARIABILITY_RMSSD', HealthDataType.HEIGHT: 'HEIGHT', HealthDataType.INSULIN_DELIVERY: 'INSULIN_DELIVERY', HealthDataType.RESTING_HEART_RATE: 'RESTING_HEART_RATE', diff --git a/packages/health/lib/src/heath_data_types.dart b/packages/health/lib/src/heath_data_types.dart index 8730c7771..ba04a21e2 100644 --- a/packages/health/lib/src/heath_data_types.dart +++ b/packages/health/lib/src/heath_data_types.dart @@ -54,6 +54,7 @@ enum HealthDataType { FORCED_EXPIRATORY_VOLUME, HEART_RATE, HEART_RATE_VARIABILITY_SDNN, + HEART_RATE_VARIABILITY_RMSSD, HEIGHT, INSULIN_DELIVERY, RESTING_HEART_RATE, @@ -221,6 +222,7 @@ const List dataTypeKeysAndroid = [ HealthDataType.BODY_TEMPERATURE, HealthDataType.BODY_WATER_MASS, HealthDataType.HEART_RATE, + HealthDataType.HEART_RATE_VARIABILITY_RMSSD, HealthDataType.HEIGHT, HealthDataType.STEPS, HealthDataType.WEIGHT, @@ -348,6 +350,7 @@ const Map dataTypeToUnit = { HealthDataType.LOW_HEART_RATE_EVENT: HealthDataUnit.NO_UNIT, HealthDataType.IRREGULAR_HEART_RATE_EVENT: HealthDataUnit.NO_UNIT, HealthDataType.HEART_RATE_VARIABILITY_SDNN: HealthDataUnit.MILLISECOND, + HealthDataType.HEART_RATE_VARIABILITY_RMSSD: HealthDataUnit.MILLISECOND, HealthDataType.ELECTROCARDIOGRAM: HealthDataUnit.VOLT, HealthDataType.NUTRITION: HealthDataUnit.NO_UNIT, From e3a4530496351c9ba960e503e58ad28d7f2d32c3 Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Tue, 6 Aug 2024 14:31:25 +0200 Subject: [PATCH 19/80] Remove duplicate lines in README --- packages/health/README.md | 138 -------------------------------------- 1 file changed, 138 deletions(-) diff --git a/packages/health/README.md b/packages/health/README.md index c908a2ad0..f5d4a6437 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -508,141 +508,3 @@ The plugin supports the following [`HealthWorkoutActivityType`](https://pub.dev/ | WINDSURFING | | yes | | | | ZUMBA | | yes | | | | OTHER | yes | yes | | | -| -------------------------------- | ---------------- | -------------- | ------------------------- | ----------------------------------------------------------------- | -| ARCHERY | yes | yes | | | -| BADMINTON | yes | yes | yes | | -| BASEBALL | yes | yes | yes | | -| BASKETBALL | yes | yes | yes | | -| BIKING | yes | yes | yes | on iOS this is CYCLING, but name changed here to fit with Android | -| BOXING | yes | yes | yes | | -| CRICKET | yes | yes | yes | | -| CURLING | yes | yes | | | -| ELLIPTICAL | yes | yes | yes | | -| FENCING | yes | yes | yes | | -| AMERICAN_FOOTBALL | yes | yes | yes | | -| AUSTRALIAN_FOOTBALL | yes | yes | yes | | -| SOCCER | yes | yes | | | -| GOLF | yes | yes | yes | | -| GYMNASTICS | yes | yes | yes | | -| HANDBALL | yes | yes | yes | | -| HIGH_INTENSITY_INTERVAL_TRAINING | yes | yes | yes | | -| HIKING | yes | yes | yes | | -| HOCKEY | yes | yes | | | -| SKATING | yes | yes | yes | On iOS this is skating_sports | -| JUMP_ROPE | yes | yes | | | -| KICKBOXING | yes | yes | | | -| MARTIAL_ARTS | yes | yes | yes | | -| PILATES | yes | yes | yes | | -| RACQUETBALL | yes | yes | yes | | -| RUGBY | yes | yes | yes | | -| RUNNING | yes | yes | yes | | -| ROWING | yes | yes | yes | | -| SAILING | yes | yes | yes | | -| CROSS_COUNTRY_SKIING | yes | yes | | | -| DOWNHILL_SKIING | yes | yes | | | -| SNOWBOARDING | yes | yes | yes | | -| SOFTBALL | yes | yes | yes | | -| SQUASH | yes | yes | yes | | -| STAIR_CLIMBING | yes | yes | yes | | -| SWIMMING | yes | yes | | | -| TABLE_TENNIS | yes | yes | yes | | -| TENNIS | yes | yes | yes | | -| VOLLEYBALL | yes | yes | yes | | -| WALKING | yes | yes | yes | | -| WATER_POLO | yes | yes | yes | | -| YOGA | yes | yes | yes | | -| BOWLING | yes | | | | -| CROSS_TRAINING | yes | | | | -| TRACK_AND_FIELD | yes | | | | -| DISC_SPORTS | yes | | | | -| LACROSSE | yes | | | | -| PREPARATION_AND_RECOVERY | yes | | | | -| FLEXIBILITY | yes | | | | -| COOLDOWN | yes | | | | -| WHEELCHAIR_WALK_PACE | yes | | | | -| WHEELCHAIR_RUN_PACE | yes | | | | -| HAND_CYCLING | yes | | | | -| CORE_TRAINING | yes | | | | -| FUNCTIONAL_STRENGTH_TRAINING | yes | | | | -| TRADITIONAL_STRENGTH_TRAINING | yes | | | | -| MIXED_CARDIO | yes | | | | -| STAIRS | yes | | | | -| STEP_TRAINING | yes | | | | -| FITNESS_GAMING | yes | | | | -| BARRE | yes | | | | -| CARDIO_DANCE | yes | | | | -| SOCIAL_DANCE | yes | | | | -| MIND_AND_BODY | yes | | | | -| PICKLEBALL | yes | | | | -| CLIMBING | yes | | | | -| EQUESTRIAN_SPORTS | yes | | | | -| FISHING | yes | | | | -| HUNTING | yes | | | | -| PLAY | yes | | | | -| SNOW_SPORTS | yes | | | | -| PADDLE_SPORTS | yes | | | | -| SURFING_SPORTS | yes | | | | -| WATER_FITNESS | yes | | | | -| WATER_SPORTS | yes | | | | -| TAI_CHI | yes | | | | -| WRESTLING | yes | | | | -| AEROBICS | | yes | | | -| BIATHLON | | yes | | | -| CALISTHENICS | | yes | yes | | -| CIRCUIT_TRAINING | | yes | | | -| CROSS_FIT | | yes | | | -| DANCING | | yes | yes | | -| DIVING | | yes | | | -| ELEVATOR | | yes | | | -| ERGOMETER | | yes | | | -| ESCALATOR | | yes | | | -| FRISBEE_DISC | | yes | yes | | -| GARDENING | | yes | | | -| GUIDED_BREATHING | | yes | yes | | -| HORSEBACK_RIDING | | yes | | | -| HOUSEWORK | | yes | | | -| INTERVAL_TRAINING | | yes | | | -| IN_VEHICLE | | yes | | | -| KAYAKING | | yes | | | -| KETTLEBELL_TRAINING | | yes | | | -| KICK_SCOOTER | | yes | | | -| KITE_SURFING | | yes | | | -| MEDITATION | | yes | | | -| MIXED_MARTIAL_ARTS | | yes | | | -| P90X | | yes | | | -| PARAGLIDING | | yes | yes | | -| POLO | | yes | | | -| ROCK_CLIMBING | (yes) | yes | yes | on iOS this will be stored as CLIMBING | -| RUNNING_JOGGING | (yes) | yes | | on iOS this will be stored as RUNNING | -| RUNNING_SAND | (yes) | yes | | on iOS this will be stored as RUNNING | -| RUNNING_TREADMILL | (yes) | yes | yes | on iOS this will be stored as RUNNING | -| SCUBA_DIVING | | yes | yes | | -| SKATING_CROSS | (yes) | yes | | on iOS this will be stored as SKATING | -| SKATING_INDOOR | (yes) | yes | | on iOS this will be stored as SKATING | -| SKATING_INLINE | (yes) | yes | | on iOS this will be stored as SKATING | -| SKIING_BACK_COUNTRY | | yes | | | -| SKIING_KITE | | yes | | | -| SKIING_ROLLER | | yes | | | -| SLEDDING | | yes | | | -| STAIR_CLIMBING_MACHINE | | yes | yes | | -| STANDUP_PADDLEBOARDING | | yes | | | -| STILL | | yes | | | -| STRENGTH_TRAINING | | yes | yes | | -| SURFING | | yes | yes | | -| SWIMMING_OPEN_WATER | | yes | yes | | -| SWIMMING_POOL | | yes | yes | | -| TEAM_SPORTS | | yes | | | -| TILTING | | yes | | | -| TREADMILL | | yes | | | -| VOLLEYBALL_BEACH | | yes | | | -| VOLLEYBALL_INDOOR | | yes | | | -| WAKEBOARDING | | yes | | | -| WALKING_FITNESS | | yes | | | -| WALKING_NORDIC | | yes | | | -| WALKING_STROLLER | | yes | | | -| WALKING_TREADMILL | | yes | | | -| WEIGHTLIFTING | | yes | yes | | -| WHEELCHAIR | | yes | yes | | -| WINDSURFING | | yes | | | -| ZUMBA | | yes | | | -| OTHER | yes | yes | | | From 310d13d8f91b6481b82389a8f558c1010678b5c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bijok?= <71793107+PawelBijok@users.noreply.github.com> Date: Fri, 9 Aug 2024 14:39:37 +0200 Subject: [PATCH 20/80] [HEALTH]: Add default activity type when there is no match (#1016) --- packages/health/lib/src/health_value_types.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/health/lib/src/health_value_types.dart b/packages/health/lib/src/health_value_types.dart index d3d364141..6c161ac1e 100644 --- a/packages/health/lib/src/health_value_types.dart +++ b/packages/health/lib/src/health_value_types.dart @@ -153,7 +153,9 @@ class WorkoutHealthValue extends HealthValue { factory WorkoutHealthValue.fromHealthDataPoint(dynamic dataPoint) => WorkoutHealthValue( workoutActivityType: HealthWorkoutActivityType.values.firstWhere( - (element) => element.name == dataPoint['workoutActivityType']), + (element) => element.name == dataPoint['workoutActivityType'], + orElse: () => HealthWorkoutActivityType.OTHER, + ), totalEnergyBurned: dataPoint['totalEnergyBurned'] != null ? (dataPoint['totalEnergyBurned'] as num).toInt() : null, From e076c8eb93bd855478fda611c56bb69935ed8509 Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Thu, 22 Aug 2024 11:13:12 +0200 Subject: [PATCH 21/80] [Health]: Remove Google Fit support (#1014) * Remove Google Fit and imports from Android code * Formatting * Remove Google Fit column from readme * Remove support for Google Fit types not supported by Health Connect * Remove more Google Fit workout types * Remove references to Google Fit, remove `useHealthConnectIfAvailable` * Remove `disconect` method channel * Remove `flowRate` from `writeBloodOxygen` as it is not supported in Health Connect * Remove more unsupported workout types * Add missing import * Remove Google Fit as dependency * Add notice in README * Improve logging for HC permission callback * Update some documentation * Android: Fix `requestAuthorization` not returning a result on success * Remove additional workout types that are not supported * Remove another workout type * Add missing unimplemented method * small updates to the README * Fix an issue in generated file * When writing data, check if the type is available on the requested platform --------- Co-authored-by: bardram --- packages/health/README.md | 446 +- packages/health/android/build.gradle | 3 - .../cachet/plugins/health/HealthPlugin.kt | 6338 ++++++----------- .../health/example/android/app/build.gradle | 4 - packages/health/example/lib/main.dart | 12 +- packages/health/example/lib/util.dart | 3 +- .../ios/Classes/SwiftHealthPlugin.swift | 12 - packages/health/ios/health.podspec | 4 +- packages/health/lib/health.g.dart | 110 +- packages/health/lib/health.json.dart | 1 - .../health/lib/src/health_data_point.dart | 4 +- packages/health/lib/src/health_plugin.dart | 217 +- .../health/lib/src/health_value_types.dart | 2 +- packages/health/lib/src/heath_data_types.dart | 107 +- packages/health/pubspec.yaml | 2 +- 15 files changed, 2495 insertions(+), 4770 deletions(-) diff --git a/packages/health/README.md b/packages/health/README.md index f5d4a6437..ccd2d913c 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -1,8 +1,8 @@ # Health -Enables reading and writing health data from/to Apple Health, Google Fit and Health Connect. +Enables reading and writing health data from/to Apple Health and Health Connect. -> Google Fitness API is deprecated and will be turned down in 2024, thus this package will also transition to only support Health Connect. +> **NOTE:** Google has deprecated the Google Fit API. According to the [documentation](https://developers.google.com/fit/android), as of **May 1st 2014** developers cannot sign up for using the API. As such, this package has removed support for Google Fit as of version 11.0.0 and users are urged to upgrade as soon as possible. The plugin supports: @@ -17,7 +17,7 @@ The plugin supports: - cleaning up duplicate data points via the `removeDuplicates` method. - removing data of a given type in a selected period of time using the `delete` method. -Note that for Android, the target phone **needs** to have [Google Fit](https://www.google.com/fit/) or [Health Connect](https://health.google/health-connect-android/) (which is currently in beta) installed and have access to the internet, otherwise this plugin will not work. +Note that for Android, the target phone **needs** to have [Health Connect](https://health.google/health-connect-android/) (which is currently in beta) installed and have access to the internet, otherwise this plugin will not work. See the tables below for supported health and workout data types. @@ -25,7 +25,7 @@ See the tables below for supported health and workout data types. ### Apple Health (iOS) -Step 1: Append the `Info.plist` with the following 2 entries +First, add the following 2 entries to the `Info.plist`: ```xml NSHealthShareUsageDescription @@ -34,59 +34,9 @@ Step 1: Append the `Info.plist` with the following 2 entries We will sync your data with the Apple Health app to give you better insights ``` -Step 2: Open your Flutter project in Xcode by right clicking on the "ios" folder and selecting "Open in Xcode". Next, enable "HealthKit" by adding a capability inside the "Signing & Capabilities" tab of the Runner target's settings. +Then, open your Flutter project in Xcode by right clicking on the "ios" folder and selecting "Open in Xcode". Next, enable "HealthKit" by adding a capability inside the "Signing & Capabilities" tab of the Runner target's settings. -### Android - -Starting from API level 28 (Android 9.0) accessing some fitness data (e.g. Steps) requires a special permission. To set it add the following line to your `AndroidManifest.xml` file. - -```xml - -``` - -Additionally, for workouts, if the distance of a workout is requested then the location permissions below are needed. - -```xml - - -``` - -#### Google Fit (Android option 1) - -Follow the guide at . Below is an example of following the guide. - -Change directory to your key-store directory (MacOS): - -`cd ~/.android/` - -Get your keystore SHA1 fingerprint: - -`keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android` - -Example output: - -```bash -Alias name: androiddebugkey -Creation date: Jan 01, 2013 -Entry type: PrivateKeyEntry -Certificate chain length: 1 -Certificate[1]: -Owner: CN=Android Debug, O=Android, C=US -Issuer: CN=Android Debug, O=Android, C=US -Serial number: 4aa9b300 -Valid from: Mon Jan 01 08:04:04 UTC 2013 until: Mon Jan 01 18:04:04 PST 2033 -Certificate fingerprints: - MD5: AE:9F:95:D0:A6:86:89:BC:A8:70:BA:34:FF:6A:AC:F9 - SHA1: BB:0D:AC:74:D3:21:E1:43:07:71:9B:62:90:AF:A1:66:6E:44:5D:75 - Signature algorithm name: SHA1withRSA - Version: 3 -``` - -Follow the instructions at for setting up an OAuth2 Client ID for a Google project, and adding the SHA1 fingerprint to that OAuth2 credential. - -The client id will look something like `YOUR_CLIENT_ID.apps.googleusercontent.com`. - -#### Health Connect (Android option 2) +### Google Health Connect (Android) Health Connect requires the following lines in the `AndroidManifest.xml` file (see also the example app): @@ -115,17 +65,38 @@ In the Health Connect permissions activity there is a link to your privacy polic ``` -If using Health Connect on Android it requires special permissions in the `AndroidManifest.xml` file. The permissions can be found here: +For each data type you want to access, the READ and WRITE permissions need to be added to the `AndroidManifest.xml` file. The list of [permissions](https://developer.android.com/health-and-fitness/guides/health-connect/plan/data-types#permissions) can be found here on the [data types](https://developer.android.com/health-and-fitness/guides/health-connect/plan/data-types) page. -Example shown here (can also be found in the example app): +An example of asking for permission to read and write heart rate data is shown below and more examples can also be found in the example app. ```xml -... ``` -Furthermore, an `intent-filter` needs to be added to the `.MainActivity` activity. +Accessing fitness data (e.g. Steps) requires permission to access the "Activity Recognition" API. To set it add the following line to your `AndroidManifest.xml` file. + +```xml + +``` + +Additionally, for workouts, if the distance of a workout is requested then the location permissions below are needed. + +```xml + + +``` + +Because this is labeled as a `dangerous` protection level, the permission system will not grant it automatically and it requires the user's action. +You can prompt the user for it using the [permission_handler](https://pub.dev/packages/permission_handler) plugin. +Follow the plugin setup instructions and add the following line before requesting the data: + +```dart +await Permission.activityRecognition.request(); +await Permission.location.request(); +``` + +Finally, an `intent-filter` needs to be added to the `.MainActivity` activity. ```xml >? = - null - private lateinit var healthConnectClient: HealthConnectClient - private lateinit var scope: CoroutineScope - - private var BODY_FAT_PERCENTAGE = "BODY_FAT_PERCENTAGE" - private var HEIGHT = "HEIGHT" - private var WEIGHT = "WEIGHT" - private var STEPS = "STEPS" - private var AGGREGATE_STEP_COUNT = "AGGREGATE_STEP_COUNT" - private var ACTIVE_ENERGY_BURNED = "ACTIVE_ENERGY_BURNED" - private var HEART_RATE = "HEART_RATE" - private var BODY_TEMPERATURE = "BODY_TEMPERATURE" - private var BODY_WATER_MASS = "BODY_WATER_MASS" - private var BLOOD_PRESSURE_SYSTOLIC = "BLOOD_PRESSURE_SYSTOLIC" - private var BLOOD_PRESSURE_DIASTOLIC = "BLOOD_PRESSURE_DIASTOLIC" - private var BLOOD_OXYGEN = "BLOOD_OXYGEN" - private var BLOOD_GLUCOSE = "BLOOD_GLUCOSE" - private var HEART_RATE_VARIABILITY_RMSSD = "HEART_RATE_VARIABILITY_RMSSD" - private var MOVE_MINUTES = "MOVE_MINUTES" - private var DISTANCE_DELTA = "DISTANCE_DELTA" - private var WATER = "WATER" - private var RESTING_HEART_RATE = "RESTING_HEART_RATE" - private var BASAL_ENERGY_BURNED = "BASAL_ENERGY_BURNED" - private var FLIGHTS_CLIMBED = "FLIGHTS_CLIMBED" - private var RESPIRATORY_RATE = "RESPIRATORY_RATE" - private var MENSTRUATION_FLOW = "MENSTRUATION_FLOW" - - // TODO support unknown? - private var SLEEP_ASLEEP = "SLEEP_ASLEEP" - private var SLEEP_AWAKE = "SLEEP_AWAKE" - private var SLEEP_IN_BED = "SLEEP_IN_BED" - private var SLEEP_SESSION = "SLEEP_SESSION" - private var SLEEP_LIGHT = "SLEEP_LIGHT" - private var SLEEP_DEEP = "SLEEP_DEEP" - private var SLEEP_REM = "SLEEP_REM" - private var SLEEP_OUT_OF_BED = "SLEEP_OUT_OF_BED" - private var WORKOUT = "WORKOUT" - private var NUTRITION = "NUTRITION" - private var BREAKFAST = "BREAKFAST" - private var LUNCH = "LUNCH" - private var DINNER = "DINNER" - private var SNACK = "SNACK" - private var MEAL_UNKNOWN = "UNKNOWN" - - private var TOTAL_CALORIES_BURNED = "TOTAL_CALORIES_BURNED" - - val workoutTypeMap = - mapOf( - "AEROBICS" to FitnessActivities.AEROBICS, - "AMERICAN_FOOTBALL" to FitnessActivities.FOOTBALL_AMERICAN, - "ARCHERY" to FitnessActivities.ARCHERY, - "AUSTRALIAN_FOOTBALL" to - FitnessActivities.FOOTBALL_AUSTRALIAN, - "BADMINTON" to FitnessActivities.BADMINTON, - "BASEBALL" to FitnessActivities.BASEBALL, - "BASKETBALL" to FitnessActivities.BASKETBALL, - "BIATHLON" to FitnessActivities.BIATHLON, - "BIKING" to FitnessActivities.BIKING, - "BIKING_HAND" to FitnessActivities.BIKING_HAND, - "BIKING_MOUNTAIN" to FitnessActivities.BIKING_MOUNTAIN, - "BIKING_ROAD" to FitnessActivities.BIKING_ROAD, - "BIKING_SPINNING" to FitnessActivities.BIKING_SPINNING, - "BIKING_STATIONARY" to FitnessActivities.BIKING_STATIONARY, - "BIKING_UTILITY" to FitnessActivities.BIKING_UTILITY, - "BOXING" to FitnessActivities.BOXING, - "CALISTHENICS" to FitnessActivities.CALISTHENICS, - "CIRCUIT_TRAINING" to FitnessActivities.CIRCUIT_TRAINING, - "CRICKET" to FitnessActivities.CRICKET, - "CROSS_COUNTRY_SKIING" to - FitnessActivities.SKIING_CROSS_COUNTRY, - "CROSS_FIT" to FitnessActivities.CROSSFIT, - "CURLING" to FitnessActivities.CURLING, - "DANCING" to FitnessActivities.DANCING, - "DIVING" to FitnessActivities.DIVING, - "DOWNHILL_SKIING" to FitnessActivities.SKIING_DOWNHILL, - "ELEVATOR" to FitnessActivities.ELEVATOR, - "ELLIPTICAL" to FitnessActivities.ELLIPTICAL, - "ERGOMETER" to FitnessActivities.ERGOMETER, - "ESCALATOR" to FitnessActivities.ESCALATOR, - "FENCING" to FitnessActivities.FENCING, - "FRISBEE_DISC" to FitnessActivities.FRISBEE_DISC, - "GARDENING" to FitnessActivities.GARDENING, - "GOLF" to FitnessActivities.GOLF, - "GUIDED_BREATHING" to FitnessActivities.GUIDED_BREATHING, - "GYMNASTICS" to FitnessActivities.GYMNASTICS, - "HANDBALL" to FitnessActivities.HANDBALL, - "HIGH_INTENSITY_INTERVAL_TRAINING" to - FitnessActivities - .HIGH_INTENSITY_INTERVAL_TRAINING, - "HIKING" to FitnessActivities.HIKING, - "HOCKEY" to FitnessActivities.HOCKEY, - "HORSEBACK_RIDING" to FitnessActivities.HORSEBACK_RIDING, - "HOUSEWORK" to FitnessActivities.HOUSEWORK, - "IN_VEHICLE" to FitnessActivities.IN_VEHICLE, - "ICE_SKATING" to FitnessActivities.ICE_SKATING, - "INTERVAL_TRAINING" to FitnessActivities.INTERVAL_TRAINING, - "JUMP_ROPE" to FitnessActivities.JUMP_ROPE, - "KAYAKING" to FitnessActivities.KAYAKING, - "KETTLEBELL_TRAINING" to - FitnessActivities.KETTLEBELL_TRAINING, - "KICK_SCOOTER" to FitnessActivities.KICK_SCOOTER, - "KICKBOXING" to FitnessActivities.KICKBOXING, - "KITE_SURFING" to FitnessActivities.KITESURFING, - "MARTIAL_ARTS" to FitnessActivities.MARTIAL_ARTS, - "MEDITATION" to FitnessActivities.MEDITATION, - "MIXED_MARTIAL_ARTS" to - FitnessActivities.MIXED_MARTIAL_ARTS, - "P90X" to FitnessActivities.P90X, - "PARAGLIDING" to FitnessActivities.PARAGLIDING, - "PILATES" to FitnessActivities.PILATES, - "POLO" to FitnessActivities.POLO, - "RACQUETBALL" to FitnessActivities.RACQUETBALL, - "ROCK_CLIMBING" to FitnessActivities.ROCK_CLIMBING, - "ROWING" to FitnessActivities.ROWING, - "ROWING_MACHINE" to FitnessActivities.ROWING_MACHINE, - "RUGBY" to FitnessActivities.RUGBY, - "RUNNING_JOGGING" to FitnessActivities.RUNNING_JOGGING, - "RUNNING_SAND" to FitnessActivities.RUNNING_SAND, - "RUNNING_TREADMILL" to FitnessActivities.RUNNING_TREADMILL, - "RUNNING" to FitnessActivities.RUNNING, - "SAILING" to FitnessActivities.SAILING, - "SCUBA_DIVING" to FitnessActivities.SCUBA_DIVING, - "SKATING_CROSS" to FitnessActivities.SKATING_CROSS, - "SKATING_INDOOR" to FitnessActivities.SKATING_INDOOR, - "SKATING_INLINE" to FitnessActivities.SKATING_INLINE, - "SKATING" to FitnessActivities.SKATING, - "SKIING" to FitnessActivities.SKIING, - "SKIING_BACK_COUNTRY" to - FitnessActivities.SKIING_BACK_COUNTRY, - "SKIING_KITE" to FitnessActivities.SKIING_KITE, - "SKIING_ROLLER" to FitnessActivities.SKIING_ROLLER, - "SLEDDING" to FitnessActivities.SLEDDING, - "SNOWBOARDING" to FitnessActivities.SNOWBOARDING, - "SNOWMOBILE" to FitnessActivities.SNOWMOBILE, - "SNOWSHOEING" to FitnessActivities.SNOWSHOEING, - "SOCCER" to FitnessActivities.FOOTBALL_SOCCER, - "SOFTBALL" to FitnessActivities.SOFTBALL, - "SQUASH" to FitnessActivities.SQUASH, - "STAIR_CLIMBING_MACHINE" to - FitnessActivities.STAIR_CLIMBING_MACHINE, - "STAIR_CLIMBING" to FitnessActivities.STAIR_CLIMBING, - "STANDUP_PADDLEBOARDING" to - FitnessActivities.STANDUP_PADDLEBOARDING, - "STILL" to FitnessActivities.STILL, - "STRENGTH_TRAINING" to FitnessActivities.STRENGTH_TRAINING, - "SURFING" to FitnessActivities.SURFING, - "SWIMMING_OPEN_WATER" to - FitnessActivities.SWIMMING_OPEN_WATER, - "SWIMMING_POOL" to FitnessActivities.SWIMMING_POOL, - "SWIMMING" to FitnessActivities.SWIMMING, - "TABLE_TENNIS" to FitnessActivities.TABLE_TENNIS, - "TEAM_SPORTS" to FitnessActivities.TEAM_SPORTS, - "TENNIS" to FitnessActivities.TENNIS, - "TILTING" to FitnessActivities.TILTING, - "VOLLEYBALL_BEACH" to FitnessActivities.VOLLEYBALL_BEACH, - "VOLLEYBALL_INDOOR" to FitnessActivities.VOLLEYBALL_INDOOR, - "VOLLEYBALL" to FitnessActivities.VOLLEYBALL, - "WAKEBOARDING" to FitnessActivities.WAKEBOARDING, - "WALKING_FITNESS" to FitnessActivities.WALKING_FITNESS, - "WALKING_PACED" to FitnessActivities.WALKING_PACED, - "WALKING_NORDIC" to FitnessActivities.WALKING_NORDIC, - "WALKING_STROLLER" to FitnessActivities.WALKING_STROLLER, - "WALKING_TREADMILL" to FitnessActivities.WALKING_TREADMILL, - "WALKING" to FitnessActivities.WALKING, - "WATER_POLO" to FitnessActivities.WATER_POLO, - "WEIGHTLIFTING" to FitnessActivities.WEIGHTLIFTING, - "WHEELCHAIR" to FitnessActivities.WHEELCHAIR, - "WINDSURFING" to FitnessActivities.WINDSURFING, - "YOGA" to FitnessActivities.YOGA, - "ZUMBA" to FitnessActivities.ZUMBA, - "OTHER" to FitnessActivities.OTHER, - ) - - // TODO: Update with new workout types when Health Connect becomes the standard. - val workoutTypeMapHealthConnect = - mapOf( - // "AEROBICS" to - // ExerciseSessionRecord.EXERCISE_TYPE_AEROBICS, - "AMERICAN_FOOTBALL" to - ExerciseSessionRecord - .EXERCISE_TYPE_FOOTBALL_AMERICAN, - // "ARCHERY" to ExerciseSessionRecord.EXERCISE_TYPE_ARCHERY, - "AUSTRALIAN_FOOTBALL" to - ExerciseSessionRecord - .EXERCISE_TYPE_FOOTBALL_AUSTRALIAN, - "BADMINTON" to - ExerciseSessionRecord - .EXERCISE_TYPE_BADMINTON, - "BASEBALL" to ExerciseSessionRecord.EXERCISE_TYPE_BASEBALL, - "BASKETBALL" to - ExerciseSessionRecord - .EXERCISE_TYPE_BASKETBALL, - // "BIATHLON" to - // ExerciseSessionRecord.EXERCISE_TYPE_BIATHLON, - "BIKING" to ExerciseSessionRecord.EXERCISE_TYPE_BIKING, - // "BIKING_HAND" to - // ExerciseSessionRecord.EXERCISE_TYPE_BIKING_HAND, - // "BIKING_MOUNTAIN" to - // ExerciseSessionRecord.EXERCISE_TYPE_BIKING_MOUNTAIN, - // "BIKING_ROAD" to - // ExerciseSessionRecord.EXERCISE_TYPE_BIKING_ROAD, - // "BIKING_SPINNING" to - // ExerciseSessionRecord.EXERCISE_TYPE_BIKING_SPINNING, - // "BIKING_STATIONARY" to - // ExerciseSessionRecord.EXERCISE_TYPE_BIKING_STATIONARY, - // "BIKING_UTILITY" to - // ExerciseSessionRecord.EXERCISE_TYPE_BIKING_UTILITY, - "BOXING" to ExerciseSessionRecord.EXERCISE_TYPE_BOXING, - "CALISTHENICS" to - ExerciseSessionRecord - .EXERCISE_TYPE_CALISTHENICS, - // "CIRCUIT_TRAINING" to - // ExerciseSessionRecord.EXERCISE_TYPE_CIRCUIT_TRAINING, - "CRICKET" to ExerciseSessionRecord.EXERCISE_TYPE_CRICKET, - // "CROSS_COUNTRY_SKIING" to - // ExerciseSessionRecord.EXERCISE_TYPE_SKIING_CROSS_COUNTRY, - // "CROSS_FIT" to - // ExerciseSessionRecord.EXERCISE_TYPE_CROSSFIT, - // "CURLING" to ExerciseSessionRecord.EXERCISE_TYPE_CURLING, - "DANCING" to ExerciseSessionRecord.EXERCISE_TYPE_DANCING, - // "DIVING" to ExerciseSessionRecord.EXERCISE_TYPE_DIVING, - // "DOWNHILL_SKIING" to - // ExerciseSessionRecord.EXERCISE_TYPE_SKIING_DOWNHILL, - // "ELEVATOR" to - // ExerciseSessionRecord.EXERCISE_TYPE_ELEVATOR, - "ELLIPTICAL" to - ExerciseSessionRecord - .EXERCISE_TYPE_ELLIPTICAL, - // "ERGOMETER" to - // ExerciseSessionRecord.EXERCISE_TYPE_ERGOMETER, - // "ESCALATOR" to - // ExerciseSessionRecord.EXERCISE_TYPE_ESCALATOR, - "FENCING" to ExerciseSessionRecord.EXERCISE_TYPE_FENCING, - "FRISBEE_DISC" to - ExerciseSessionRecord - .EXERCISE_TYPE_FRISBEE_DISC, - // "GARDENING" to - // ExerciseSessionRecord.EXERCISE_TYPE_GARDENING, - "GOLF" to ExerciseSessionRecord.EXERCISE_TYPE_GOLF, - "GUIDED_BREATHING" to - ExerciseSessionRecord - .EXERCISE_TYPE_GUIDED_BREATHING, - "GYMNASTICS" to - ExerciseSessionRecord - .EXERCISE_TYPE_GYMNASTICS, - "HANDBALL" to ExerciseSessionRecord.EXERCISE_TYPE_HANDBALL, - "HIGH_INTENSITY_INTERVAL_TRAINING" to - ExerciseSessionRecord - .EXERCISE_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING, - "HIKING" to ExerciseSessionRecord.EXERCISE_TYPE_HIKING, - // "HOCKEY" to ExerciseSessionRecord.EXERCISE_TYPE_HOCKEY, - // "HORSEBACK_RIDING" to - // ExerciseSessionRecord.EXERCISE_TYPE_HORSEBACK_RIDING, - // "HOUSEWORK" to - // ExerciseSessionRecord.EXERCISE_TYPE_HOUSEWORK, - // "IN_VEHICLE" to - // ExerciseSessionRecord.EXERCISE_TYPE_IN_VEHICLE, - "ICE_SKATING" to - ExerciseSessionRecord - .EXERCISE_TYPE_ICE_SKATING, - // "INTERVAL_TRAINING" to - // ExerciseSessionRecord.EXERCISE_TYPE_INTERVAL_TRAINING, - // "JUMP_ROPE" to - // ExerciseSessionRecord.EXERCISE_TYPE_JUMP_ROPE, - // "KAYAKING" to - // ExerciseSessionRecord.EXERCISE_TYPE_KAYAKING, - // "KETTLEBELL_TRAINING" to - // ExerciseSessionRecord.EXERCISE_TYPE_KETTLEBELL_TRAINING, - // "KICK_SCOOTER" to - // ExerciseSessionRecord.EXERCISE_TYPE_KICK_SCOOTER, - // "KICKBOXING" to - // ExerciseSessionRecord.EXERCISE_TYPE_KICKBOXING, - // "KITE_SURFING" to - // ExerciseSessionRecord.EXERCISE_TYPE_KITESURFING, - "MARTIAL_ARTS" to - ExerciseSessionRecord - .EXERCISE_TYPE_MARTIAL_ARTS, - // "MEDITATION" to - // ExerciseSessionRecord.EXERCISE_TYPE_MEDITATION, - // "MIXED_MARTIAL_ARTS" to - // ExerciseSessionRecord.EXERCISE_TYPE_MIXED_MARTIAL_ARTS, - // "P90X" to ExerciseSessionRecord.EXERCISE_TYPE_P90X, - "PARAGLIDING" to - ExerciseSessionRecord - .EXERCISE_TYPE_PARAGLIDING, - "PILATES" to ExerciseSessionRecord.EXERCISE_TYPE_PILATES, - // "POLO" to ExerciseSessionRecord.EXERCISE_TYPE_POLO, - "RACQUETBALL" to - ExerciseSessionRecord - .EXERCISE_TYPE_RACQUETBALL, - "ROCK_CLIMBING" to - ExerciseSessionRecord - .EXERCISE_TYPE_ROCK_CLIMBING, - "ROWING" to ExerciseSessionRecord.EXERCISE_TYPE_ROWING, - "ROWING_MACHINE" to - ExerciseSessionRecord - .EXERCISE_TYPE_ROWING_MACHINE, - "RUGBY" to ExerciseSessionRecord.EXERCISE_TYPE_RUGBY, - // "RUNNING_JOGGING" to - // ExerciseSessionRecord.EXERCISE_TYPE_RUNNING_JOGGING, - // "RUNNING_SAND" to - // ExerciseSessionRecord.EXERCISE_TYPE_RUNNING_SAND, - "RUNNING_TREADMILL" to - ExerciseSessionRecord - .EXERCISE_TYPE_RUNNING_TREADMILL, - "RUNNING" to ExerciseSessionRecord.EXERCISE_TYPE_RUNNING, - "SAILING" to ExerciseSessionRecord.EXERCISE_TYPE_SAILING, - "SCUBA_DIVING" to - ExerciseSessionRecord - .EXERCISE_TYPE_SCUBA_DIVING, - // "SKATING_CROSS" to - // ExerciseSessionRecord.EXERCISE_TYPE_SKATING_CROSS, - // "SKATING_INDOOR" to - // ExerciseSessionRecord.EXERCISE_TYPE_SKATING_INDOOR, - // "SKATING_INLINE" to - // ExerciseSessionRecord.EXERCISE_TYPE_SKATING_INLINE, - "SKATING" to ExerciseSessionRecord.EXERCISE_TYPE_SKATING, - "SKIING" to ExerciseSessionRecord.EXERCISE_TYPE_SKIING, - // "SKIING_BACK_COUNTRY" to - // ExerciseSessionRecord.EXERCISE_TYPE_SKIING_BACK_COUNTRY, - // "SKIING_KITE" to - // ExerciseSessionRecord.EXERCISE_TYPE_SKIING_KITE, - // "SKIING_ROLLER" to - // ExerciseSessionRecord.EXERCISE_TYPE_SKIING_ROLLER, - // "SLEDDING" to - // ExerciseSessionRecord.EXERCISE_TYPE_SLEDDING, - "SNOWBOARDING" to - ExerciseSessionRecord - .EXERCISE_TYPE_SNOWBOARDING, - // "SNOWMOBILE" to - // ExerciseSessionRecord.EXERCISE_TYPE_SNOWMOBILE, - "SNOWSHOEING" to - ExerciseSessionRecord - .EXERCISE_TYPE_SNOWSHOEING, - // "SOCCER" to - // ExerciseSessionRecord.EXERCISE_TYPE_FOOTBALL_SOCCER, - "SOFTBALL" to ExerciseSessionRecord.EXERCISE_TYPE_SOFTBALL, - "SQUASH" to ExerciseSessionRecord.EXERCISE_TYPE_SQUASH, - "STAIR_CLIMBING_MACHINE" to - ExerciseSessionRecord - .EXERCISE_TYPE_STAIR_CLIMBING_MACHINE, - "STAIR_CLIMBING" to - ExerciseSessionRecord - .EXERCISE_TYPE_STAIR_CLIMBING, - // "STANDUP_PADDLEBOARDING" to - // ExerciseSessionRecord.EXERCISE_TYPE_STANDUP_PADDLEBOARDING, - // "STILL" to ExerciseSessionRecord.EXERCISE_TYPE_STILL, - "STRENGTH_TRAINING" to - ExerciseSessionRecord - .EXERCISE_TYPE_STRENGTH_TRAINING, - "SURFING" to ExerciseSessionRecord.EXERCISE_TYPE_SURFING, - "SWIMMING_OPEN_WATER" to - ExerciseSessionRecord - .EXERCISE_TYPE_SWIMMING_OPEN_WATER, - "SWIMMING_POOL" to - ExerciseSessionRecord - .EXERCISE_TYPE_SWIMMING_POOL, - // "SWIMMING" to - // ExerciseSessionRecord.EXERCISE_TYPE_SWIMMING, - "TABLE_TENNIS" to - ExerciseSessionRecord - .EXERCISE_TYPE_TABLE_TENNIS, - // "TEAM_SPORTS" to - // ExerciseSessionRecord.EXERCISE_TYPE_TEAM_SPORTS, - "TENNIS" to ExerciseSessionRecord.EXERCISE_TYPE_TENNIS, - // "TILTING" to ExerciseSessionRecord.EXERCISE_TYPE_TILTING, - // "VOLLEYBALL_BEACH" to - // ExerciseSessionRecord.EXERCISE_TYPE_VOLLEYBALL_BEACH, - // "VOLLEYBALL_INDOOR" to - // ExerciseSessionRecord.EXERCISE_TYPE_VOLLEYBALL_INDOOR, - "VOLLEYBALL" to - ExerciseSessionRecord - .EXERCISE_TYPE_VOLLEYBALL, - // "WAKEBOARDING" to - // ExerciseSessionRecord.EXERCISE_TYPE_WAKEBOARDING, - // "WALKING_FITNESS" to - // ExerciseSessionRecord.EXERCISE_TYPE_WALKING_FITNESS, - // "WALKING_PACED" to - // ExerciseSessionRecord.EXERCISE_TYPE_WALKING_PACED, - // "WALKING_NORDIC" to - // ExerciseSessionRecord.EXERCISE_TYPE_WALKING_NORDIC, - // "WALKING_STROLLER" to - // ExerciseSessionRecord.EXERCISE_TYPE_WALKING_STROLLER, - // "WALKING_TREADMILL" to - // ExerciseSessionRecord.EXERCISE_TYPE_WALKING_TREADMILL, - "WALKING" to ExerciseSessionRecord.EXERCISE_TYPE_WALKING, - "WATER_POLO" to - ExerciseSessionRecord - .EXERCISE_TYPE_WATER_POLO, - "WEIGHTLIFTING" to - ExerciseSessionRecord - .EXERCISE_TYPE_WEIGHTLIFTING, - "WHEELCHAIR" to - ExerciseSessionRecord - .EXERCISE_TYPE_WHEELCHAIR, - // "WINDSURFING" to - // ExerciseSessionRecord.EXERCISE_TYPE_WINDSURFING, - "YOGA" to ExerciseSessionRecord.EXERCISE_TYPE_YOGA, - // "ZUMBA" to ExerciseSessionRecord.EXERCISE_TYPE_ZUMBA, - // "OTHER" to ExerciseSessionRecord.EXERCISE_TYPE_OTHER, - ) - - override fun onAttachedToEngine( - @NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding - ) { - scope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL_NAME) - channel?.setMethodCallHandler(this) - context = flutterPluginBinding.applicationContext - threadPoolExecutor = Executors.newFixedThreadPool(4) - checkAvailability() - if (healthConnectAvailable) { - healthConnectClient = - HealthConnectClient.getOrCreate( - flutterPluginBinding.applicationContext - ) - } - } - - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { - channel = null - activity = null - threadPoolExecutor!!.shutdown() - threadPoolExecutor = null - } - - // This static function is optional and equivalent to onAttachedToEngine. It supports the - // old - // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting - // plugin registration via this function while apps migrate to use the new Android APIs - // post-flutter-1.12 via https://flutter.dev/go/android-project-migration. - // - // It is encouraged to share logic between onAttachedToEngine and registerWith to keep - // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be - // called - // depending on the user's project. onAttachedToEngine or registerWith must both be defined - // in the same class. - companion object { - @Suppress("unused") - @JvmStatic - fun registerWith(registrar: Registrar) { - val channel = MethodChannel(registrar.messenger(), CHANNEL_NAME) - val plugin = HealthPlugin(channel) - registrar.addActivityResultListener(plugin) - channel.setMethodCallHandler(plugin) - } - } - - override fun success(p0: Any?) { - handler?.post { mResult?.success(p0) } - } - - override fun notImplemented() { - handler?.post { mResult?.notImplemented() } - } - - override fun error( - errorCode: String, - errorMessage: String?, - errorDetails: Any?, - ) { - handler?.post { mResult?.error(errorCode, errorMessage, errorDetails) } - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { - if (requestCode == GOOGLE_FIT_PERMISSIONS_REQUEST_CODE) { - if (resultCode == Activity.RESULT_OK) { - Log.i("FLUTTER_HEALTH", "Access Granted!") - mResult?.success(true) - } else if (resultCode == Activity.RESULT_CANCELED) { - Log.i("FLUTTER_HEALTH", "Access Denied!") - mResult?.success(false) - } - } - return false - } - - private fun onHealthConnectPermissionCallback(permissionGranted: Set) { - if (permissionGranted.isEmpty()) { - mResult?.success(false) - Log.i("FLUTTER_HEALTH", "Access Denied (to Health Connect)!") - } else { - mResult?.success(true) - Log.i("FLUTTER_HEALTH", "Access Granted (to Health Connect)!") - } - } - - private fun keyToHealthDataType(type: String): DataType { - return when (type) { - BODY_FAT_PERCENTAGE -> DataType.TYPE_BODY_FAT_PERCENTAGE - HEIGHT -> DataType.TYPE_HEIGHT - WEIGHT -> DataType.TYPE_WEIGHT - STEPS -> DataType.TYPE_STEP_COUNT_DELTA - AGGREGATE_STEP_COUNT -> DataType.AGGREGATE_STEP_COUNT_DELTA - ACTIVE_ENERGY_BURNED -> DataType.TYPE_CALORIES_EXPENDED - HEART_RATE -> DataType.TYPE_HEART_RATE_BPM - BODY_TEMPERATURE -> HealthDataTypes.TYPE_BODY_TEMPERATURE - BLOOD_PRESSURE_SYSTOLIC -> HealthDataTypes.TYPE_BLOOD_PRESSURE - BLOOD_PRESSURE_DIASTOLIC -> HealthDataTypes.TYPE_BLOOD_PRESSURE - BLOOD_OXYGEN -> HealthDataTypes.TYPE_OXYGEN_SATURATION - BLOOD_GLUCOSE -> HealthDataTypes.TYPE_BLOOD_GLUCOSE - MOVE_MINUTES -> DataType.TYPE_MOVE_MINUTES - DISTANCE_DELTA -> DataType.TYPE_DISTANCE_DELTA - WATER -> DataType.TYPE_HYDRATION - SLEEP_ASLEEP -> DataType.TYPE_SLEEP_SEGMENT - SLEEP_AWAKE -> DataType.TYPE_SLEEP_SEGMENT - SLEEP_IN_BED -> DataType.TYPE_SLEEP_SEGMENT - SLEEP_LIGHT -> DataType.TYPE_SLEEP_SEGMENT - SLEEP_REM -> DataType.TYPE_SLEEP_SEGMENT - SLEEP_DEEP -> DataType.TYPE_SLEEP_SEGMENT - WORKOUT -> DataType.TYPE_ACTIVITY_SEGMENT - NUTRITION -> DataType.TYPE_NUTRITION - else -> throw IllegalArgumentException("Unsupported dataType: $type") - } - } - - private fun getField(type: String): Field { - return when (type) { - BODY_FAT_PERCENTAGE -> Field.FIELD_PERCENTAGE - HEIGHT -> Field.FIELD_HEIGHT - WEIGHT -> Field.FIELD_WEIGHT - STEPS -> Field.FIELD_STEPS - ACTIVE_ENERGY_BURNED -> Field.FIELD_CALORIES - HEART_RATE -> Field.FIELD_BPM - BODY_TEMPERATURE -> HealthFields.FIELD_BODY_TEMPERATURE - BLOOD_PRESSURE_SYSTOLIC -> HealthFields.FIELD_BLOOD_PRESSURE_SYSTOLIC - BLOOD_PRESSURE_DIASTOLIC -> HealthFields.FIELD_BLOOD_PRESSURE_DIASTOLIC - BLOOD_OXYGEN -> HealthFields.FIELD_OXYGEN_SATURATION - BLOOD_GLUCOSE -> HealthFields.FIELD_BLOOD_GLUCOSE_LEVEL - MOVE_MINUTES -> Field.FIELD_DURATION - DISTANCE_DELTA -> Field.FIELD_DISTANCE - WATER -> Field.FIELD_VOLUME - SLEEP_ASLEEP -> Field.FIELD_SLEEP_SEGMENT_TYPE - SLEEP_AWAKE -> Field.FIELD_SLEEP_SEGMENT_TYPE - SLEEP_IN_BED -> Field.FIELD_SLEEP_SEGMENT_TYPE - SLEEP_LIGHT -> Field.FIELD_SLEEP_SEGMENT_TYPE - SLEEP_REM -> Field.FIELD_SLEEP_SEGMENT_TYPE - SLEEP_DEEP -> Field.FIELD_SLEEP_SEGMENT_TYPE - WORKOUT -> Field.FIELD_ACTIVITY - NUTRITION -> Field.FIELD_NUTRIENTS - else -> throw IllegalArgumentException("Unsupported dataType: $type") - } - } - - private fun isIntField(dataSource: DataSource, unit: Field): Boolean { - val dataPoint = DataPoint.builder(dataSource).build() - val value = dataPoint.getValue(unit) - return value.format == Field.FORMAT_INT32 - } - - // / Extracts the (numeric) value from a Health Data Point - private fun getHealthDataValue(dataPoint: DataPoint, field: Field): Any { - val value = dataPoint.getValue(field) - // Conversion is needed because glucose is stored as mmoll in Google Fit; - // while mgdl is used for glucose in this plugin. - val isGlucose = field == HealthFields.FIELD_BLOOD_GLUCOSE_LEVEL - return when (value.format) { - Field.FORMAT_FLOAT -> - if (!isGlucose) value.asFloat() - else value.asFloat() * MMOLL_2_MGDL - Field.FORMAT_INT32 -> value.asInt() - Field.FORMAT_STRING -> value.asString() - else -> Log.e("Unsupported format:", value.format.toString()) - } - } - - /** Delete records of the given type in the time range */ - private fun delete(call: MethodCall, result: Result) { - if (useHealthConnectIfAvailable && healthConnectAvailable) { - deleteHCData(call, result) - return - } - if (context == null) { - result.success(false) - return - } - - val type = call.argument("dataTypeKey")!! - val startTime = call.argument("startTime")!! - val endTime = call.argument("endTime")!! - - // Look up data type and unit for the type key - val dataType = keyToHealthDataType(type) - val field = getField(type) - - val typesBuilder = FitnessOptions.builder() - typesBuilder.addDataType(dataType, FitnessOptions.ACCESS_WRITE) - - val dataSource = - DataDeleteRequest.Builder() - .setTimeInterval( - startTime, - endTime, - TimeUnit.MILLISECONDS - ) - .addDataType(dataType) - .deleteAllSessions() - .build() - - val fitnessOptions = typesBuilder.build() - - try { - val googleSignInAccount = - GoogleSignIn.getAccountForExtension( - context!!.applicationContext, - fitnessOptions - ) - Fitness.getHistoryClient(context!!.applicationContext, googleSignInAccount) - .deleteData(dataSource) - .addOnSuccessListener { - Log.i( - "FLUTTER_HEALTH::SUCCESS", - "Dataset deleted successfully!" - ) - result.success(true) - } - .addOnFailureListener( - errHandler( - result, - "There was an error deleting the dataset" - ) - ) - } catch (e3: Exception) { - result.success(false) - } - } - - /** Save a Blood Pressure measurement with systolic and diastolic values */ - private fun writeBloodPressure(call: MethodCall, result: Result) { - if (useHealthConnectIfAvailable && healthConnectAvailable) { - writeBloodPressureHC(call, result) - return - } - if (context == null) { - result.success(false) - return - } - - val dataType = HealthDataTypes.TYPE_BLOOD_PRESSURE - val systolic = call.argument("systolic")!! - val diastolic = call.argument("diastolic")!! - val startTime = call.argument("startTime")!! - val endTime = call.argument("endTime")!! - - val typesBuilder = FitnessOptions.builder() - typesBuilder.addDataType(dataType, FitnessOptions.ACCESS_WRITE) - - val dataSource = - DataSource.Builder() - .setDataType(dataType) - .setType(DataSource.TYPE_RAW) - .setDevice( - Device.getLocalDevice( - context!!.applicationContext - ) - ) - .setAppPackageName(context!!.applicationContext) - .build() - - val builder = - DataPoint.builder(dataSource) - .setTimeInterval( - startTime, - endTime, - TimeUnit.MILLISECONDS - ) - .setField( - HealthFields.FIELD_BLOOD_PRESSURE_SYSTOLIC, - systolic - ) - .setField( - HealthFields.FIELD_BLOOD_PRESSURE_DIASTOLIC, - diastolic - ) - .build() - - val dataPoint = builder - val dataSet = DataSet.builder(dataSource).add(dataPoint).build() - - val fitnessOptions = typesBuilder.build() - try { - val googleSignInAccount = - GoogleSignIn.getAccountForExtension( - context!!.applicationContext, - fitnessOptions - ) - Fitness.getHistoryClient(context!!.applicationContext, googleSignInAccount) - .insertData(dataSet) - .addOnSuccessListener { - Log.i( - "FLUTTER_HEALTH::SUCCESS", - "Blood Pressure added successfully!" - ) - result.success(true) - } - .addOnFailureListener( - errHandler( - result, - "There was an error adding the blood pressure data!", - ), - ) - } catch (e3: Exception) { - result.success(false) - } - } - - private fun writeMealHC(call: MethodCall, result: Result) { - val startTime = Instant.ofEpochMilli(call.argument("start_time")!!) - val endTime = Instant.ofEpochMilli(call.argument("end_time")!!) - val calories = call.argument("calories") - val protein = call.argument("protein") as Double? - val carbs = call.argument("carbs") as Double? - val fat = call.argument("fat") as Double? - val caffeine = call.argument("caffeine") as Double? - val vitaminA = call.argument("vitamin_a") as Double? - val b1Thiamine = call.argument("b1_thiamine") as Double? - val b2Riboflavin = call.argument("b2_riboflavin") as Double? - val b3Niacin = call.argument("b3_niacin") as Double? - val b5PantothenicAcid = call.argument("b5_pantothenic_acid") as Double? - val b6Pyridoxine = call.argument("b6_pyridoxine") as Double? - val b7Biotin = call.argument("b7_biotin") as Double? - val b9Folate = call.argument("b9_folate") as Double? - val b12Cobalamin = call.argument("b12_cobalamin") as Double? - val vitaminC = call.argument("vitamin_c") as Double? - val vitaminD = call.argument("vitamin_d") as Double? - val vitaminE = call.argument("vitamin_e") as Double? - val vitaminK = call.argument("vitamin_k") as Double? - val calcium = call.argument("calcium") as Double? - val chloride = call.argument("chloride") as Double? - val cholesterol = call.argument("cholesterol") as Double? - // Choline is not yet supported by Health Connect - // val choline = call.argument("choline") as Double? - val chromium = call.argument("chromium") as Double? - val copper = call.argument("copper") as Double? - val fatUnsaturated = call.argument("fat_unsaturated") as Double? - val fatMonounsaturated = call.argument("fat_monounsaturated") as Double? - val fatPolyunsaturated = call.argument("fat_polyunsaturated") as Double? - val fatSaturated = call.argument("fat_saturated") as Double? - val fatTransMonoenoic = call.argument("fat_trans_monoenoic") as Double? - val fiber = call.argument("fiber") as Double? - val iodine = call.argument("iodine") as Double? - val iron = call.argument("iron") as Double? - val magnesium = call.argument("magnesium") as Double? - val manganese = call.argument("manganese") as Double? - val molybdenum = call.argument("molybdenum") as Double? - val phosphorus = call.argument("phosphorus") as Double? - val potassium = call.argument("potassium") as Double? - val selenium = call.argument("selenium") as Double? - val sodium = call.argument("sodium") as Double? - val sugar = call.argument("sugar") as Double? - // Water is not support on a food in Health Connect - // val water = call.argument("water") as Double? - val zinc = call.argument("zinc") as Double? - - val name = call.argument("name") - val mealType = call.argument("meal_type")!! - - scope.launch { - try { - val list = mutableListOf() - list.add( - NutritionRecord( - name = name, - energy = calories?.kilocalories, - totalCarbohydrate = carbs?.grams, - protein = protein?.grams, - totalFat = fat?.grams, - caffeine = caffeine?.grams, - vitaminA = vitaminA?.grams, - thiamin = b1Thiamine?.grams, - riboflavin = b2Riboflavin?.grams, - niacin = b3Niacin?.grams, - pantothenicAcid = b5PantothenicAcid?.grams, - vitaminB6 = b6Pyridoxine?.grams, - biotin = b7Biotin?.grams, - folate = b9Folate?.grams, - vitaminB12 = b12Cobalamin?.grams, - vitaminC = vitaminC?.grams, - vitaminD = vitaminD?.grams, - vitaminE = vitaminE?.grams, - vitaminK = vitaminK?.grams, - calcium = calcium?.grams, - chloride = chloride?.grams, - cholesterol = cholesterol?.grams, - chromium = chromium?.grams, - copper = copper?.grams, - unsaturatedFat = fatUnsaturated?.grams, - monounsaturatedFat = fatMonounsaturated?.grams, - polyunsaturatedFat = fatPolyunsaturated?.grams, - saturatedFat = fatSaturated?.grams, - transFat = fatTransMonoenoic?.grams, - dietaryFiber = fiber?.grams, - iodine = iodine?.grams, - iron = iron?.grams, - magnesium = magnesium?.grams, - manganese = manganese?.grams, - molybdenum = molybdenum?.grams, - phosphorus = phosphorus?.grams, - potassium = potassium?.grams, - selenium = selenium?.grams, - sodium = sodium?.grams, - sugar = sugar?.grams, - zinc = zinc?.grams, - startTime = startTime, - startZoneOffset = null, - endTime = endTime, - endZoneOffset = null, - mealType = - MapMealTypeToTypeHC[ - mealType] - ?: MEAL_TYPE_UNKNOWN, - ), - ) - healthConnectClient.insertRecords( - list, - ) - result.success(true) - Log.i( - "FLUTTER_HEALTH::SUCCESS", - "[Health Connect] Meal was successfully added!" - ) - } catch (e: Exception) { - Log.w( - "FLUTTER_HEALTH::ERROR", - "[Health Connect] There was an error adding the meal", - ) - Log.w("FLUTTER_HEALTH::ERROR", e.message ?: "unknown error") - Log.w("FLUTTER_HEALTH::ERROR", e.stackTrace.toString()) - result.success(false) - } - } - } - - /** Save a Nutrition measurement with calories, carbs, protein, fat, name and mealType */ - private fun writeMeal(call: MethodCall, result: Result) { - if (useHealthConnectIfAvailable && healthConnectAvailable) { - writeMealHC(call, result) - return - } - - if (context == null) { - result.success(false) - return - } - - val startTime = call.argument("start_time")!! - val endTime = call.argument("end_time")!! - val calories = call.argument("calories") - val carbs = call.argument("carbs") as Double? - val protein = call.argument("protein") as Double? - val fat = call.argument("fat") as Double? - - - val name = call.argument("name") - val mealType = call.argument("meal_type")!! - - val dataType = DataType.TYPE_NUTRITION - - val typesBuilder = FitnessOptions.builder() - typesBuilder.addDataType(dataType, FitnessOptions.ACCESS_WRITE) - - val dataSource = - DataSource.Builder() - .setDataType(dataType) - .setType(DataSource.TYPE_RAW) - .setDevice( - Device.getLocalDevice( - context!!.applicationContext - ) - ) - .setAppPackageName(context!!.applicationContext) - .build() - - val nutrients = mutableMapOf(Field.NUTRIENT_CALORIES to calories?.toFloat()) - - if (carbs != null) { - nutrients[Field.NUTRIENT_TOTAL_CARBS] = carbs.toFloat() - } - - if (protein != null) { - nutrients[Field.NUTRIENT_PROTEIN] = protein.toFloat() - } - - if (fat != null) { - nutrients[Field.NUTRIENT_TOTAL_FAT] = fat.toFloat() - } - - val dataBuilder = - DataPoint.builder(dataSource) - .setTimeInterval( - startTime, - endTime, - TimeUnit.MILLISECONDS - ) - .setField( - Field.FIELD_NUTRIENTS, - // Remove null values - nutrients.filterValues { it != null }.toMutableMap(), - ) - - if (name != null) { - dataBuilder.setField(Field.FIELD_FOOD_ITEM, name as String) - } - - dataBuilder.setField( - Field.FIELD_MEAL_TYPE, - MapMealTypeToType[mealType] ?: Field.MEAL_TYPE_UNKNOWN - ) - - val dataPoint = dataBuilder.build() - - val dataSet = DataSet.builder(dataSource).add(dataPoint).build() - - val fitnessOptions = typesBuilder.build() - try { - val googleSignInAccount = - GoogleSignIn.getAccountForExtension( - context!!.applicationContext, - fitnessOptions - ) - Fitness.getHistoryClient(context!!.applicationContext, googleSignInAccount) - .insertData(dataSet) - .addOnSuccessListener { - Log.i( - "FLUTTER_HEALTH::SUCCESS", - "Meal added successfully!" - ) - result.success(true) - } - .addOnFailureListener( - errHandler( - result, - "There was an error adding the meal data!" - ) - ) - } catch (e3: Exception) { - result.success(false) - } - } - - /** Save a data type in Google Fit */ - private fun writeData(call: MethodCall, result: Result) { - if (useHealthConnectIfAvailable && healthConnectAvailable) { - writeHCData(call, result) - return - } - if (context == null) { - result.success(false) - return - } - - val type = call.argument("dataTypeKey")!! - val startTime = call.argument("startTime")!! - val endTime = call.argument("endTime")!! - val value = call.argument("value")!! - - // Look up data type and unit for the type key - val dataType = keyToHealthDataType(type) - val field = getField(type) - - val typesBuilder = FitnessOptions.builder() - typesBuilder.addDataType(dataType, FitnessOptions.ACCESS_WRITE) - - val dataSource = - DataSource.Builder() - .setDataType(dataType) - .setType(DataSource.TYPE_RAW) - .setDevice( - Device.getLocalDevice( - context!!.applicationContext - ) - ) - .setAppPackageName(context!!.applicationContext) - .build() - - val builder = - if (startTime == endTime) { - DataPoint.builder(dataSource) - .setTimestamp( - startTime, - TimeUnit.MILLISECONDS - ) - } else { - DataPoint.builder(dataSource) - .setTimeInterval( - startTime, - endTime, - TimeUnit.MILLISECONDS - ) - } - - // Conversion is needed because glucose is stored as mmoll in Google Fit; - // while mgdl is used for glucose in this plugin. - val isGlucose = field == HealthFields.FIELD_BLOOD_GLUCOSE_LEVEL - val dataPoint = - if (!isIntField(dataSource, field)) { - builder.setField( - field, - (if (!isGlucose) value - else - (value / - MMOLL_2_MGDL) - .toFloat()) - ) - .build() - } else { - builder.setField(field, value.toInt()).build() - } - - val dataSet = DataSet.builder(dataSource).add(dataPoint).build() - - if (dataType == DataType.TYPE_SLEEP_SEGMENT) { - typesBuilder.accessSleepSessions(FitnessOptions.ACCESS_READ) - } - val fitnessOptions = typesBuilder.build() - try { - val googleSignInAccount = - GoogleSignIn.getAccountForExtension( - context!!.applicationContext, - fitnessOptions - ) - Fitness.getHistoryClient(context!!.applicationContext, googleSignInAccount) - .insertData(dataSet) - .addOnSuccessListener { - Log.i( - "FLUTTER_HEALTH::SUCCESS", - "Dataset added successfully!" - ) - result.success(true) - } - .addOnFailureListener( - errHandler( - result, - "There was an error adding the dataset" - ) - ) - } catch (e3: Exception) { - result.success(false) - } - } - - /** - * Save menstrual flow data - */ - private fun writeMenstruationFlow(call: MethodCall, result: Result) { - if (useHealthConnectIfAvailable && healthConnectAvailable) { - writeHCData(call, result) - return - } - } - - /** - * Save the blood oxygen saturation, in Google Fit with the supplemental flow rate, in - * HealthConnect without - */ - private fun writeBloodOxygen(call: MethodCall, result: Result) { - // Health Connect does not support supplemental flow rate, thus it is ignored - if (useHealthConnectIfAvailable && healthConnectAvailable) { - writeHCData(call, result) - return - } - - if (context == null) { - result.success(false) - return - } - - val dataType = HealthDataTypes.TYPE_OXYGEN_SATURATION - val startTime = call.argument("startTime")!! - val endTime = call.argument("endTime")!! - val saturation = call.argument("value")!! - val flowRate = call.argument("flowRate")!! - - val typesBuilder = FitnessOptions.builder() - typesBuilder.addDataType(dataType, FitnessOptions.ACCESS_WRITE) - - val dataSource = - DataSource.Builder() - .setDataType(dataType) - .setType(DataSource.TYPE_RAW) - .setDevice( - Device.getLocalDevice( - context!!.applicationContext - ) - ) - .setAppPackageName(context!!.applicationContext) - .build() - - val builder = - if (startTime == endTime) { - DataPoint.builder(dataSource) - .setTimestamp( - startTime, - TimeUnit.MILLISECONDS - ) - } else { - DataPoint.builder(dataSource) - .setTimeInterval( - startTime, - endTime, - TimeUnit.MILLISECONDS - ) - } - - builder.setField(HealthFields.FIELD_SUPPLEMENTAL_OXYGEN_FLOW_RATE, flowRate) - builder.setField(HealthFields.FIELD_OXYGEN_SATURATION, saturation) - - val dataPoint = builder.build() - val dataSet = DataSet.builder(dataSource).add(dataPoint).build() - - val fitnessOptions = typesBuilder.build() - try { - val googleSignInAccount = - GoogleSignIn.getAccountForExtension( - context!!.applicationContext, - fitnessOptions - ) - Fitness.getHistoryClient(context!!.applicationContext, googleSignInAccount) - .insertData(dataSet) - .addOnSuccessListener { - Log.i( - "FLUTTER_HEALTH::SUCCESS", - "Blood Oxygen added successfully!" - ) - result.success(true) - } - .addOnFailureListener( - errHandler( - result, - "There was an error adding the blood oxygen data!", - ), - ) - } catch (e3: Exception) { - result.success(false) - } - } - - /** Save a Workout session with options for distance and calories expended */ - private fun writeWorkoutData(call: MethodCall, result: Result) { - if (useHealthConnectIfAvailable && healthConnectAvailable) { - writeWorkoutHCData(call, result) - return - } - if (context == null) { - result.success(false) - return - } - - val type = call.argument("activityType")!! - val startTime = call.argument("startTime")!! - val endTime = call.argument("endTime")!! - val totalEnergyBurned = call.argument("totalEnergyBurned") - val totalDistance = call.argument("totalDistance") - - val activityType = getActivityType(type) - // Create the Activity Segment DataSource - val activitySegmentDataSource = - DataSource.Builder() - .setAppPackageName(context!!.packageName) - .setDataType(DataType.TYPE_ACTIVITY_SEGMENT) - .setStreamName("FLUTTER_HEALTH - Activity") - .setType(DataSource.TYPE_RAW) - .build() - // Create the Activity Segment - val activityDataPoint = - DataPoint.builder(activitySegmentDataSource) - .setTimeInterval( - startTime, - endTime, - TimeUnit.MILLISECONDS - ) - .setActivityField( - Field.FIELD_ACTIVITY, - activityType - ) - .build() - // Add DataPoint to DataSet - val activitySegments = - DataSet.builder(activitySegmentDataSource) - .add(activityDataPoint) - .build() - - // If distance is provided - var distanceDataSet: DataSet? = null - if (totalDistance != null) { - // Create a data source - val distanceDataSource = - DataSource.Builder() - .setAppPackageName(context!!.packageName) - .setDataType(DataType.TYPE_DISTANCE_DELTA) - .setStreamName("FLUTTER_HEALTH - Distance") - .setType(DataSource.TYPE_RAW) - .build() - - val distanceDataPoint = - DataPoint.builder(distanceDataSource) - .setTimeInterval( - startTime, - endTime, - TimeUnit.MILLISECONDS - ) - .setField( - Field.FIELD_DISTANCE, - totalDistance.toFloat() - ) - .build() - // Create a data set - distanceDataSet = - DataSet.builder(distanceDataSource) - .add(distanceDataPoint) - .build() - } - // If energyBurned is provided - var energyDataSet: DataSet? = null - if (totalEnergyBurned != null) { - // Create a data source - val energyDataSource = - DataSource.Builder() - .setAppPackageName(context!!.packageName) - .setDataType( - DataType.TYPE_CALORIES_EXPENDED - ) - .setStreamName("FLUTTER_HEALTH - Calories") - .setType(DataSource.TYPE_RAW) - .build() - - val energyDataPoint = - DataPoint.builder(energyDataSource) - .setTimeInterval( - startTime, - endTime, - TimeUnit.MILLISECONDS - ) - .setField( - Field.FIELD_CALORIES, - totalEnergyBurned.toFloat() - ) - .build() - // Create a data set - energyDataSet = - DataSet.builder(energyDataSource) - .add(energyDataPoint) - .build() - } - - // Finish session setup - val session = - Session.Builder() - .setName( - activityType - ) // TODO: Make a sensible name / allow user to set - // name - .setDescription("") - .setIdentifier(UUID.randomUUID().toString()) - .setActivity(activityType) - .setStartTime(startTime, TimeUnit.MILLISECONDS) - .setEndTime(endTime, TimeUnit.MILLISECONDS) - .build() - // Build a session and add the values provided - val sessionInsertRequestBuilder = - SessionInsertRequest.Builder() - .setSession(session) - .addDataSet(activitySegments) - if (totalDistance != null) { - sessionInsertRequestBuilder.addDataSet(distanceDataSet!!) - } - if (totalEnergyBurned != null) { - sessionInsertRequestBuilder.addDataSet(energyDataSet!!) - } - val insertRequest = sessionInsertRequestBuilder.build() - - val fitnessOptionsBuilder = - FitnessOptions.builder() - .addDataType( - DataType.TYPE_ACTIVITY_SEGMENT, - FitnessOptions.ACCESS_WRITE - ) - if (totalDistance != null) { - fitnessOptionsBuilder.addDataType( - DataType.TYPE_DISTANCE_DELTA, - FitnessOptions.ACCESS_WRITE, - ) - } - if (totalEnergyBurned != null) { - fitnessOptionsBuilder.addDataType( - DataType.TYPE_CALORIES_EXPENDED, - FitnessOptions.ACCESS_WRITE, - ) - } - val fitnessOptions = fitnessOptionsBuilder.build() - - try { - val googleSignInAccount = - GoogleSignIn.getAccountForExtension( - context!!.applicationContext, - fitnessOptions - ) - Fitness.getSessionsClient( - context!!.applicationContext, - googleSignInAccount, - ) - .insertSession(insertRequest) - .addOnSuccessListener { - Log.i( - "FLUTTER_HEALTH::SUCCESS", - "Workout was successfully added!" - ) - result.success(true) - } - .addOnFailureListener( - errHandler( - result, - "There was an error adding the workout" - ) - ) - } catch (e: Exception) { - result.success(false) - } - } - - /** Get all datapoints of the DataType within the given time range */ - private fun getData(call: MethodCall, result: Result) { - if (useHealthConnectIfAvailable && healthConnectAvailable) { - getHCData(call, result) - return - } - - if (context == null) { - result.success(null) - return - } - - val type = call.argument("dataTypeKey")!! - val startTime = call.argument("startTime")!! - val endTime = call.argument("endTime")!! - val includeManualEntry = call.argument("includeManualEntry")!! - // Look up data type and unit for the type key - val dataType = keyToHealthDataType(type) - val field = getField(type) - val typesBuilder = FitnessOptions.builder() - typesBuilder.addDataType(dataType) - - // Add special cases for accessing workouts or sleep data. - if (dataType == DataType.TYPE_SLEEP_SEGMENT) { - typesBuilder.accessSleepSessions(FitnessOptions.ACCESS_READ) - } else if (dataType == DataType.TYPE_ACTIVITY_SEGMENT) { - typesBuilder.accessActivitySessions(FitnessOptions.ACCESS_READ) - .addDataType( - DataType.TYPE_CALORIES_EXPENDED, - FitnessOptions.ACCESS_READ - ) - .addDataType( - DataType.TYPE_DISTANCE_DELTA, - FitnessOptions.ACCESS_READ - ) - } - val fitnessOptions = typesBuilder.build() - val googleSignInAccount = - GoogleSignIn.getAccountForExtension( - context!!.applicationContext, - fitnessOptions - ) - // Handle data types - when (dataType) { - DataType.TYPE_SLEEP_SEGMENT -> { - // request to the sessions for sleep data - val request = - SessionReadRequest.Builder() - .setTimeInterval( - startTime, - endTime, - TimeUnit.MILLISECONDS - ) - .enableServerQueries() - .readSessionsFromAllApps() - .includeSleepSessions() - .build() - Fitness.getSessionsClient( - context!!.applicationContext, - googleSignInAccount - ) - .readSession(request) - .addOnSuccessListener( - threadPoolExecutor!!, - sleepDataHandler(type, result) - ) - .addOnFailureListener( - errHandler( - result, - "There was an error getting the sleeping data!", - ), - ) - } - DataType.TYPE_ACTIVITY_SEGMENT -> { - val readRequest: SessionReadRequest - val readRequestBuilder = - SessionReadRequest.Builder() - .setTimeInterval( - startTime, - endTime, - TimeUnit.MILLISECONDS - ) - .enableServerQueries() - .readSessionsFromAllApps() - .includeActivitySessions() - .read(dataType) - .read( - DataType.TYPE_CALORIES_EXPENDED - ) - - // If fine location is enabled, read distance data - if (ContextCompat.checkSelfPermission( - context!!.applicationContext, - android.Manifest.permission - .ACCESS_FINE_LOCATION, - ) == PackageManager.PERMISSION_GRANTED - ) { - readRequestBuilder.read(DataType.TYPE_DISTANCE_DELTA) - } - readRequest = readRequestBuilder.build() - Fitness.getSessionsClient( - context!!.applicationContext, - googleSignInAccount - ) - .readSession(readRequest) - .addOnSuccessListener( - threadPoolExecutor!!, - workoutDataHandler(type, result) - ) - .addOnFailureListener( - errHandler( - result, - "There was an error getting the workout data!", - ), - ) - } - else -> { - Fitness.getHistoryClient( - context!!.applicationContext, - googleSignInAccount - ) - .readData( - DataReadRequest.Builder() - .read(dataType) - .setTimeRange( - startTime, - endTime, - TimeUnit.MILLISECONDS - ) - .build(), - ) - .addOnSuccessListener( - threadPoolExecutor!!, - dataHandler( - dataType, - field, - includeManualEntry, - result - ), - ) - .addOnFailureListener( - errHandler( - result, - "There was an error getting the data!", - ), - ) - } - } - } - - private fun getIntervalData(call: MethodCall, result: Result) { - if (useHealthConnectIfAvailable && healthConnectAvailable) { - getAggregateHCData(call, result) - return - } - - if (context == null) { - result.success(null) - return - } - - val type = call.argument("dataTypeKey")!! - val startTime = call.argument("startTime")!! - val endTime = call.argument("endTime")!! - val interval = call.argument("interval")!! - val includeManualEntry = call.argument("includeManualEntry")!! - - // Look up data type and unit for the type key - val dataType = keyToHealthDataType(type) - val field = getField(type) - val typesBuilder = FitnessOptions.builder() - typesBuilder.addDataType(dataType) - if (dataType == DataType.TYPE_SLEEP_SEGMENT) { - typesBuilder.accessSleepSessions(FitnessOptions.ACCESS_READ) - } - val fitnessOptions = typesBuilder.build() - val googleSignInAccount = - GoogleSignIn.getAccountForExtension( - context!!.applicationContext, - fitnessOptions - ) - - Fitness.getHistoryClient(context!!.applicationContext, googleSignInAccount) - .readData( - DataReadRequest.Builder() - .aggregate(dataType) - .bucketByTime( - interval, - TimeUnit.SECONDS - ) - .setTimeRange( - startTime, - endTime, - TimeUnit.MILLISECONDS - ) - .build() - ) - .addOnSuccessListener( - threadPoolExecutor!!, - intervalDataHandler( - dataType, - field, - includeManualEntry, - result - ) - ) - .addOnFailureListener( - errHandler( - result, - "There was an error getting the interval data!" - ) - ) - } - - private fun getAggregateData(call: MethodCall, result: Result) { - if (context == null) { - result.success(null) - return - } - - val types = call.argument>("dataTypeKeys")!! - val startTime = call.argument("startTime")!! - val endTime = call.argument("endTime")!! - val activitySegmentDuration = call.argument("activitySegmentDuration")!! - val includeManualEntry = call.argument("includeManualEntry")!! - - val typesBuilder = FitnessOptions.builder() - for (type in types) { - val dataType = keyToHealthDataType(type) - typesBuilder.addDataType(dataType) - } - val fitnessOptions = typesBuilder.build() - val googleSignInAccount = - GoogleSignIn.getAccountForExtension( - context!!.applicationContext, - fitnessOptions - ) - - val readWorkoutsRequest = - DataReadRequest.Builder() - .bucketByActivitySegment( - activitySegmentDuration, - TimeUnit.SECONDS - ) - .setTimeRange( - startTime, - endTime, - TimeUnit.MILLISECONDS - ) - - for (type in types) { - val dataType = keyToHealthDataType(type) - readWorkoutsRequest.aggregate(dataType) - } - - Fitness.getHistoryClient(context!!.applicationContext, googleSignInAccount) - .readData(readWorkoutsRequest.build()) - .addOnSuccessListener( - threadPoolExecutor!!, - aggregateDataHandler(includeManualEntry, result) - ) - .addOnFailureListener( - errHandler( - result, - "There was an error getting the aggregate data!" - ) - ) - } - - private fun dataHandler( - dataType: DataType, - field: Field, - includeManualEntry: Boolean, - result: Result - ) = OnSuccessListener { response: DataReadResponse -> - // / Fetch all data points for the specified DataType - val dataSet = response.getDataSet(dataType) - /// For each data point, extract the contents and send them to Flutter, along with - // date and unit. - var dataPoints = dataSet.dataPoints - if (!includeManualEntry) { - dataPoints = - dataPoints.filterIndexed { _, dataPoint -> - !dataPoint.originalDataSource.streamName.contains( - "user_input" - ) - } - } - // For each data point, extract the contents and send them to Flutter, along with - // date and unit. - val healthData = - dataPoints.mapIndexed { _, dataPoint -> - return@mapIndexed hashMapOf( - "value" to - getHealthDataValue( - dataPoint, - field - ), - "date_from" to - dataPoint.getStartTime( - TimeUnit.MILLISECONDS - ), - "date_to" to - dataPoint.getEndTime( - TimeUnit.MILLISECONDS - ), - "source_name" to - (dataPoint.originalDataSource - .appPackageName - ?: (dataPoint.originalDataSource - .device - ?.model - ?: "")), - "source_id" to - dataPoint.originalDataSource - .streamIdentifier, - ) - } - Handler(context!!.mainLooper).run { result.success(healthData) } - } - - private fun errHandler(result: Result, addMessage: String) = - OnFailureListener { exception -> - Handler(context!!.mainLooper).run { result.success(null) } - Log.w("FLUTTER_HEALTH::ERROR", addMessage) - Log.w("FLUTTER_HEALTH::ERROR", exception.message ?: "unknown error") - Log.w("FLUTTER_HEALTH::ERROR", exception.stackTrace.toString()) - } - - private fun sleepDataHandler(type: String, result: Result) = - OnSuccessListener { response: SessionReadResponse -> - val healthData: MutableList> = mutableListOf() - for (session in response.sessions) { - // Return sleep time in Minutes if requested ASLEEP data - if (type == SLEEP_ASLEEP) { - healthData.add( - hashMapOf( - "value" to - session.getEndTime( - TimeUnit.MINUTES - ) - - session.getStartTime( - TimeUnit.MINUTES, - ), - "date_from" to - session.getStartTime( - TimeUnit.MILLISECONDS - ), - "date_to" to - session.getEndTime( - TimeUnit.MILLISECONDS - ), - "unit" to "MINUTES", - "source_name" to - session.appPackageName, - "source_id" to - session.identifier, - ), - ) - } - - if (type == SLEEP_IN_BED) { - val dataSets = response.getDataSet(session) - - // If the sleep session has finer granularity - // sub-components, extract them: - if (dataSets.isNotEmpty()) { - for (dataSet in dataSets) { - for (dataPoint in - dataSet.dataPoints) { - // searching OUT OF BED data - if (dataPoint.getValue( - Field.FIELD_SLEEP_SEGMENT_TYPE - ) - .asInt() != - 3 - ) { - healthData.add( - hashMapOf( - "value" to - dataPoint.getEndTime( - TimeUnit.MINUTES - ) - - dataPoint.getStartTime( - TimeUnit.MINUTES, - ), - "date_from" to - dataPoint.getStartTime( - TimeUnit.MILLISECONDS - ), - "date_to" to - dataPoint.getEndTime( - TimeUnit.MILLISECONDS - ), - "unit" to - "MINUTES", - "source_name" to - (dataPoint.originalDataSource - .appPackageName - ?: (dataPoint.originalDataSource - .device - ?.model - ?: "unknown")), - "source_id" to - dataPoint.originalDataSource - .streamIdentifier, - ), - ) - } - } - } - } else { - healthData.add( - hashMapOf( - "value" to - session.getEndTime( - TimeUnit.MINUTES - ) - - session.getStartTime( - TimeUnit.MINUTES, - ), - "date_from" to - session.getStartTime( - TimeUnit.MILLISECONDS - ), - "date_to" to - session.getEndTime( - TimeUnit.MILLISECONDS - ), - "unit" to - "MINUTES", - "source_name" to - session.appPackageName, - "source_id" to - session.identifier, - ), - ) - } - } - - if (type == SLEEP_AWAKE) { - val dataSets = response.getDataSet(session) - for (dataSet in dataSets) { - for (dataPoint in dataSet.dataPoints) { - // searching SLEEP AWAKE data - if (dataPoint.getValue( - Field.FIELD_SLEEP_SEGMENT_TYPE - ) - .asInt() == - 1 - ) { - healthData.add( - hashMapOf( - "value" to - dataPoint.getEndTime( - TimeUnit.MINUTES - ) - - dataPoint.getStartTime( - TimeUnit.MINUTES, - ), - "date_from" to - dataPoint.getStartTime( - TimeUnit.MILLISECONDS - ), - "date_to" to - dataPoint.getEndTime( - TimeUnit.MILLISECONDS - ), - "unit" to - "MINUTES", - "source_name" to - (dataPoint.originalDataSource - .appPackageName - ?: (dataPoint.originalDataSource - .device - ?.model - ?: "unknown")), - "source_id" to - dataPoint.originalDataSource - .streamIdentifier, - ), - ) - } - } - } - } - } - Handler(context!!.mainLooper).run { result.success(healthData) } - } - - private fun intervalDataHandler( - dataType: DataType, - field: Field, - includeManualEntry: Boolean, - result: Result - ) = OnSuccessListener { response: DataReadResponse -> - val healthData = mutableListOf>() - for (bucket in response.buckets) { - /// Fetch all data points for the specified DataType - // val dataSet = response.getDataSet(dataType) - for (dataSet in bucket.dataSets) { - /// For each data point, extract the contents and send them to - // Flutter, along with - // date and unit. - var dataPoints = dataSet.dataPoints - if (!includeManualEntry) { - dataPoints = - dataPoints.filterIndexed { _, dataPoint -> - !dataPoint.originalDataSource - .streamName - .contains( - "user_input" - ) - } - } - for (dataPoint in dataPoints) { - for (field in dataPoint.dataType.fields) { - val healthDataItems = - dataPoints.mapIndexed { _, dataPoint - -> - return@mapIndexed hashMapOf( - "value" to - getHealthDataValue( - dataPoint, - field - ), - "date_from" to - dataPoint.getStartTime( - TimeUnit.MILLISECONDS - ), - "date_to" to - dataPoint.getEndTime( - TimeUnit.MILLISECONDS - ), - "source_name" to - (dataPoint.originalDataSource - .appPackageName - ?: (dataPoint.originalDataSource - .device - ?.model - ?: "")), - "source_id" to - dataPoint.originalDataSource - .streamIdentifier, - "is_manual_entry" to - dataPoint.originalDataSource - .streamName - .contains( - "user_input" - ) - ) - } - healthData.addAll(healthDataItems) - } - } - } - } - Handler(context!!.mainLooper).run { result.success(healthData) } - } - - private fun aggregateDataHandler(includeManualEntry: Boolean, result: Result) = - OnSuccessListener { response: DataReadResponse -> - val healthData = mutableListOf>() - for (bucket in response.buckets) { - var sourceName: Any = "" - var sourceId: Any = "" - var isManualEntry: Any = false - var totalSteps: Any = 0 - var totalDistance: Any = 0 - var totalEnergyBurned: Any = 0 - /// Fetch all data points for the specified DataType - for (dataSet in bucket.dataSets) { - /// For each data point, extract the contents and - // send them to Flutter, - // along with date and unit. - var dataPoints = dataSet.dataPoints - if (!includeManualEntry) { - dataPoints = - dataPoints.filterIndexed { - _, - dataPoint -> - !dataPoint.originalDataSource - .streamName - .contains( - "user_input" - ) - } - } - for (dataPoint in dataPoints) { - sourceName = - (dataPoint.originalDataSource - .appPackageName - ?: (dataPoint.originalDataSource - .device - ?.model - ?: "")) - sourceId = - dataPoint.originalDataSource - .streamIdentifier - isManualEntry = - dataPoint.originalDataSource - .streamName - .contains( - "user_input" - ) - for (field in dataPoint.dataType.fields) { - when (field) { - getField(STEPS) -> { - totalSteps = - getHealthDataValue( - dataPoint, - field - ) - } - getField( - DISTANCE_DELTA - ) -> { - totalDistance = - getHealthDataValue( - dataPoint, - field - ) - } - getField( - ACTIVE_ENERGY_BURNED - ) -> { - totalEnergyBurned = - getHealthDataValue( - dataPoint, - field - ) - } - } - } - } - } - val healthDataItems = - hashMapOf( - "value" to - bucket.getEndTime( - TimeUnit.MINUTES - ) - - bucket.getStartTime( - TimeUnit.MINUTES - ), - "date_from" to - bucket.getStartTime( - TimeUnit.MILLISECONDS - ), - "date_to" to - bucket.getEndTime( - TimeUnit.MILLISECONDS - ), - "source_name" to sourceName, - "source_id" to sourceId, - "is_manual_entry" to - isManualEntry, - "workout_type" to - bucket.activity - .toLowerCase(), - "total_steps" to totalSteps, - "total_distance" to - totalDistance, - "total_energy_burned" to - totalEnergyBurned - ) - healthData.add(healthDataItems) - } - Handler(context!!.mainLooper).run { result.success(healthData) } - } - - private fun workoutDataHandler(type: String, result: Result) = - OnSuccessListener { response: SessionReadResponse -> - val healthData: MutableList> = mutableListOf() - for (session in response.sessions) { - // Look for calories and distance if they - var totalEnergyBurned = 0.0 - var totalDistance = 0.0 - for (dataSet in response.getDataSet(session)) { - if (dataSet.dataType == - DataType.TYPE_CALORIES_EXPENDED - ) { - for (dataPoint in dataSet.dataPoints) { - totalEnergyBurned += - dataPoint.getValue( - Field.FIELD_CALORIES - ) - .toString() - .toDouble() - } - } - if (dataSet.dataType == DataType.TYPE_DISTANCE_DELTA - ) { - for (dataPoint in dataSet.dataPoints) { - totalDistance += - dataPoint.getValue( - Field.FIELD_DISTANCE - ) - .toString() - .toDouble() - } - } - } - healthData.add( - hashMapOf( - "workoutActivityType" to - (workoutTypeMap - .filterValues { - it == - session.activity - } - .keys - .firstOrNull() - ?: "OTHER"), - "totalEnergyBurned" to - if (totalEnergyBurned == - 0.0 - ) - null - else - totalEnergyBurned, - "totalEnergyBurnedUnit" to - "KILOCALORIE", - "totalDistance" to - if (totalDistance == - 0.0 - ) - null - else - totalDistance, - "totalDistanceUnit" to - "METER", - "date_from" to - session.getStartTime( - TimeUnit.MILLISECONDS - ), - "date_to" to - session.getEndTime( - TimeUnit.MILLISECONDS - ), - "unit" to "MINUTES", - "source_name" to - session.appPackageName, - "source_id" to - session.identifier, - ), - ) - } - Handler(context!!.mainLooper).run { result.success(healthData) } - } - - private fun callToHealthTypes(call: MethodCall): FitnessOptions { - val typesBuilder = FitnessOptions.builder() - val args = call.arguments as HashMap<*, *> - val types = (args["types"] as? ArrayList<*>)?.filterIsInstance() - val permissions = (args["permissions"] as? ArrayList<*>)?.filterIsInstance() - - assert(types != null) - assert(permissions != null) - assert(types!!.count() == permissions!!.count()) - - for ((i, typeKey) in types.withIndex()) { - val access = permissions[i] - val dataType = keyToHealthDataType(typeKey) - when (access) { - 0 -> typesBuilder.addDataType(dataType, FitnessOptions.ACCESS_READ) - 1 -> typesBuilder.addDataType(dataType, FitnessOptions.ACCESS_WRITE) - 2 -> { - typesBuilder.addDataType( - dataType, - FitnessOptions.ACCESS_READ - ) - typesBuilder.addDataType( - dataType, - FitnessOptions.ACCESS_WRITE - ) - } - else -> - throw IllegalArgumentException( - "Unknown access type $access" - ) - } - if (typeKey == SLEEP_ASLEEP || - typeKey == SLEEP_AWAKE || - typeKey == SLEEP_IN_BED - ) { - typesBuilder.accessSleepSessions(FitnessOptions.ACCESS_READ) - when (access) { - 0 -> - typesBuilder.accessSleepSessions( - FitnessOptions.ACCESS_READ - ) - 1 -> - typesBuilder.accessSleepSessions( - FitnessOptions.ACCESS_WRITE - ) - 2 -> { - typesBuilder.accessSleepSessions( - FitnessOptions.ACCESS_READ - ) - typesBuilder.accessSleepSessions( - FitnessOptions.ACCESS_WRITE - ) - } - else -> - throw IllegalArgumentException( - "Unknown access type $access" - ) - } - } - if (typeKey == WORKOUT) { - when (access) { - 0 -> - typesBuilder.accessActivitySessions( - FitnessOptions.ACCESS_READ - ) - 1 -> - typesBuilder.accessActivitySessions( - FitnessOptions.ACCESS_WRITE - ) - 2 -> { - typesBuilder.accessActivitySessions( - FitnessOptions.ACCESS_READ - ) - typesBuilder.accessActivitySessions( - FitnessOptions.ACCESS_WRITE - ) - } - else -> - throw IllegalArgumentException( - "Unknown access type $access" - ) - } - } - } - return typesBuilder.build() - } - - private fun hasPermissions(call: MethodCall, result: Result) { - if (useHealthConnectIfAvailable && healthConnectAvailable) { - hasPermissionsHC(call, result) - return - } - if (context == null) { - result.success(false) - return - } - - val optionsToRegister = callToHealthTypes(call) - val isGranted = - GoogleSignIn.hasPermissions( - GoogleSignIn.getLastSignedInAccount(context!!), - optionsToRegister, - ) - - result?.success(isGranted) +class HealthPlugin(private var channel: MethodChannel? = null) : + MethodCallHandler, ActivityResultListener, Result, ActivityAware, FlutterPlugin { + private var mResult: Result? = null + private var handler: Handler? = null + private var activity: Activity? = null + private var context: Context? = null + private var threadPoolExecutor: ExecutorService? = null + private var healthConnectRequestPermissionsLauncher: ActivityResultLauncher>? = + null + private lateinit var healthConnectClient: HealthConnectClient + private lateinit var scope: CoroutineScope + + + override fun onAttachedToEngine( + @NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding + ) { + scope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL_NAME) + channel?.setMethodCallHandler(this) + context = flutterPluginBinding.applicationContext + threadPoolExecutor = Executors.newFixedThreadPool(4) + checkAvailability() + if (healthConnectAvailable) { + healthConnectClient = + HealthConnectClient.getOrCreate( + flutterPluginBinding.applicationContext + ) } - - /** - * Requests authorization for the HealthDataTypes with the the READ or READ_WRITE permission - * type. - */ - private fun requestAuthorization(call: MethodCall, result: Result) { - if (context == null) { - result.success(false) - return - } - mResult = result - - if (useHealthConnectIfAvailable && healthConnectAvailable) { - requestAuthorizationHC(call, result) - return - } - - val optionsToRegister = callToHealthTypes(call) - - // Set to false due to bug described in - // https://github.com/cph-cachet/flutter-plugins/issues/640#issuecomment-1366830132 - val isGranted = false - - // If not granted then ask for permission - if (!isGranted && activity != null) { - GoogleSignIn.requestPermissions( - activity!!, - GOOGLE_FIT_PERMISSIONS_REQUEST_CODE, - GoogleSignIn.getLastSignedInAccount(context!!), - optionsToRegister, - ) - } else { // / Permission already granted - result?.success(true) - } + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + channel = null + activity = null + threadPoolExecutor!!.shutdown() + threadPoolExecutor = null + } + + // This static function is optional and equivalent to onAttachedToEngine. It supports the + // old + // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting + // plugin registration via this function while apps migrate to use the new Android APIs + // post-flutter-1.12 via https://flutter.dev/go/android-project-migration. + // + // It is encouraged to share logic between onAttachedToEngine and registerWith to keep + // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be + // called + // depending on the user's project. onAttachedToEngine or registerWith must both be defined + // in the same class. + companion object { + @Suppress("unused") + @JvmStatic + fun registerWith(registrar: Registrar) { + val channel = MethodChannel(registrar.messenger(), CHANNEL_NAME) + val plugin = HealthPlugin(channel) + registrar.addActivityResultListener(plugin) + channel.setMethodCallHandler(plugin) } - - /** - * Revokes access to Health Connect using `revokeAllPermissions` and Google Fit using the `disableFit`-method. - * - * Note: Using the `revokeAccess` creates a bug on android when trying to reapply for - * permissions afterwards, hence `disableFit` was used. - * Note: When using `revokePermissions` with Health Connect, the app must be completely killed - * for it to take effect. - */ - private fun revokePermissions(call: MethodCall, result: Result) { - if (useHealthConnectIfAvailable && healthConnectAvailable) { - scope.launch { - Log.i("Health", "Disabling Health Connect") - healthConnectClient.permissionController.revokeAllPermissions() - } - result.success(true) - } - if (context == null) { - result.success(false) - return - } - Fitness.getConfigClient( - activity!!, - GoogleSignIn.getLastSignedInAccount(context!!)!! - ) - .disableFit() - .addOnSuccessListener { - Log.i("Health", "Disabled Google Fit") - result.success(true) - } - .addOnFailureListener { e -> - Log.w( - "Health", - "There was an error disabling Google Fit", - e - ) - result.success(false) - } + } + + override fun success(p0: Any?) { + handler?.post { mResult?.success(p0) } + } + + override fun notImplemented() { + handler?.post { mResult?.notImplemented() } + } + + override fun error( + errorCode: String, + errorMessage: String?, + errorDetails: Any?, + ) { + handler?.post { mResult?.error(errorCode, errorMessage, errorDetails) } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { + return false + } + + /** Handle calls from the MethodChannel */ + override fun onMethodCall(call: MethodCall, result: Result) { + when (call.method) { + "installHealthConnect" -> installHealthConnect(call, result) + "getHealthConnectSdkStatus" -> getHealthConnectSdkStatus(call, result) + "hasPermissions" -> hasPermissions(call, result) + "requestAuthorization" -> requestAuthorization(call, result) + "revokePermissions" -> revokePermissions(call, result) + "getData" -> getData(call, result) + "getIntervalData" -> getIntervalData(call, result) + "writeData" -> writeData(call, result) + "delete" -> deleteData(call, result) + "getAggregateData" -> getAggregateData(call, result) + "getTotalStepsInInterval" -> getTotalStepsInInterval(call, result) + "writeWorkoutData" -> writeWorkoutData(call, result) + "writeBloodPressure" -> writeBloodPressure(call, result) + "writeBloodOxygen" -> writeBloodOxygen(call, result) + "writeMenstruationFlow" -> writeMenstruationFlow(call, result) + "writeMeal" -> writeMeal(call, result) + else -> result.notImplemented() } + } - private fun getTotalStepsInInterval(call: MethodCall, result: Result) { - val start = call.argument("startTime")!! - val end = call.argument("endTime")!! - val includeManualEntry = call.argument("includeManualEntry")!! - - if (useHealthConnectIfAvailable && healthConnectAvailable) { - getStepsHealthConnect(start, end, result) - return - } - - val context = context ?: return - - val stepsDataType = keyToHealthDataType(STEPS) - val aggregatedDataType = keyToHealthDataType(AGGREGATE_STEP_COUNT) - - val fitnessOptions = - FitnessOptions.builder() - .addDataType(stepsDataType) - .addDataType(aggregatedDataType) - .build() - val gsa = GoogleSignIn.getAccountForExtension(context, fitnessOptions) - - val ds = - DataSource.Builder() - .setAppPackageName("com.google.android.gms") - .setDataType(stepsDataType) - .setType(DataSource.TYPE_DERIVED) - .setStreamName("estimated_steps") - .build() - - val duration = (end - start).toInt() - - val request = - DataReadRequest.Builder() - .read(ds) - .bucketByTime(duration, TimeUnit.MILLISECONDS) - .setTimeRange(start, end, TimeUnit.MILLISECONDS) - .build() - - Fitness.getHistoryClient(context, gsa) - .readData(request) - .addOnFailureListener( - errHandler( - result, - "There was an error getting the total steps in the interval!", - ), - ) - .addOnSuccessListener( - threadPoolExecutor!!, - getStepsInRange( - start, - end, - includeManualEntry, - result - ), - ) + override fun onAttachedToActivity(binding: ActivityPluginBinding) { + if (channel == null) { + return } + binding.addActivityResultListener(this) + activity = binding.activity - private fun getStepsHealthConnect(start: Long, end: Long, result: Result) = - scope.launch { - try { - val startInstant = Instant.ofEpochMilli(start) - val endInstant = Instant.ofEpochMilli(end) - val response = - healthConnectClient.aggregate( - AggregateRequest( - metrics = - setOf( - StepsRecord.COUNT_TOTAL - ), - timeRangeFilter = - TimeRangeFilter.between( - startInstant, - endInstant - ), - ), - ) - // The result may be null if no data is available in the - // time range. - val stepsInInterval = - response[StepsRecord.COUNT_TOTAL] ?: 0L - Log.i( - "FLUTTER_HEALTH::SUCCESS", - "returning $stepsInInterval steps" - ) - result.success(stepsInInterval) - } catch (e: Exception) { - Log.e("FLUTTER_HEALTH::ERROR", "Unable to return steps due to the following exception:") - Log.e("FLUTTER_HEALTH::ERROR", Log.getStackTraceString(e)) - result.success(null) - } - } - - private fun getStepsInRange( - start: Long, - end: Long, - includeManualEntry: Boolean, - result: Result - ) = OnSuccessListener { response: DataReadResponse -> - var totalSteps = 0 // Variable to accumulate the total steps. - - for (bucket in response.buckets) { - for (dataSet in bucket.dataSets) { - var dataPoints = dataSet.dataPoints - if (!includeManualEntry) { - dataPoints = - dataPoints.filterIndexed { _, dataPoint -> - !dataPoint.originalDataSource - .streamName - .contains( - "user_input" - ) - } - } - for (dp in dataPoints) { - val streamName = dp.originalDataSource.streamName - if (!includeManualEntry && streamName.contains("user_input")) { - // Skip this data point if manual entry is not included - Log.i("FLUTTER_HEALTH::SKIPPED", "Skipping manual entry data point with stream name $streamName") - continue - } - - val count = dp.getValue(Field.FIELD_STEPS) - totalSteps += count.asInt() + val requestPermissionActivityContract = + PermissionController.createRequestPermissionResultContract() - val startTime = dp.getStartTime(TimeUnit.MILLISECONDS) - val startDate = Date(startTime) - val endDate = Date(dp.getEndTime(TimeUnit.MILLISECONDS)) - Log.i( - "FLUTTER_HEALTH::INFO", - "adding $count steps for $startDate - $endDate. Total so far: $totalSteps", - ) - } - } - } + healthConnectRequestPermissionsLauncher = + (activity as ComponentActivity).registerForActivityResult( + requestPermissionActivityContract + ) { granted -> onHealthConnectPermissionCallback(granted) } + } - if (totalSteps == 0) { - val startDay = Date(start) - val endDay = Date(end) - Log.i("FLUTTER_HEALTH::ERROR", "no steps for $startDay - $endDay") - } + override fun onDetachedFromActivityForConfigChanges() { + onDetachedFromActivity() + } - Log.i("FLUTTER_HEALTH::SUCCESS", "Final total steps in interval: $totalSteps") + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { + onAttachedToActivity(binding) + } - Handler(context!!.mainLooper).run { result.success(totalSteps) } + override fun onDetachedFromActivity() { + if (channel == null) { + return } - - /// Disconnect Google fit - private fun disconnect(call: MethodCall, result: Result) { - if (activity == null) { - result.success(false) - return - } - val context = activity!!.applicationContext - - val fitnessOptions = callToHealthTypes(call) - val googleAccount = GoogleSignIn.getAccountForExtension(context, fitnessOptions) - Fitness.getConfigClient(context, googleAccount).disableFit().continueWith { - val signinOption = - GoogleSignInOptions.Builder( - GoogleSignInOptions - .DEFAULT_SIGN_IN - ) - .requestId() - .requestEmail() - .build() - val googleSignInClient = GoogleSignIn.getClient(context, signinOption) - googleSignInClient.signOut() - result.success(true) - } + activity = null + healthConnectRequestPermissionsLauncher = null + } + + private var healthConnectAvailable = false + private var healthConnectStatus = HealthConnectClient.SDK_UNAVAILABLE + + private fun checkAvailability() { + healthConnectStatus = HealthConnectClient.getSdkStatus(context!!) + healthConnectAvailable = healthConnectStatus == HealthConnectClient.SDK_AVAILABLE + } + + private fun installHealthConnect(call: MethodCall, result: Result) { + val uriString = + "market://details?id=com.google.android.apps.healthdata&url=healthconnect%3A%2F%2Fonboarding" + context!!.startActivity( + Intent(Intent.ACTION_VIEW).apply { + setPackage("com.android.vending") + data = Uri.parse(uriString) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + putExtra("overlay", true) + putExtra("callerId", context!!.packageName) + } + ) + result.success(null) + } + + private fun onHealthConnectPermissionCallback(permissionGranted: Set) { + if (permissionGranted.isEmpty()) { + mResult?.success(false) + Log.i("FLUTTER_HEALTH", "Health Connect permissions were not granted! Make sure to declare the required permissions in the AndroidManifest.xml file.") + } else { + mResult?.success(true) + Log.i("FLUTTER_HEALTH", "${permissionGranted.size} Health Connect permissions were granted!") + + // log the permissions granted for debugging + Log.i("FLUTTER_HEALTH", "Permissions granted: $permissionGranted") } - - private fun getActivityType(type: String): String { - return workoutTypeMap[type] ?: FitnessActivities.UNKNOWN + } + + /** Save a Nutrition measurement with calories, carbs, protein, fat, name and mealType */ + private fun writeMeal(call: MethodCall, result: Result) { + val startTime = Instant.ofEpochMilli(call.argument("start_time")!!) + val endTime = Instant.ofEpochMilli(call.argument("end_time")!!) + val calories = call.argument("calories") + val protein = call.argument("protein") as Double? + val carbs = call.argument("carbs") as Double? + val fat = call.argument("fat") as Double? + val caffeine = call.argument("caffeine") as Double? + val vitaminA = call.argument("vitamin_a") as Double? + val b1Thiamine = call.argument("b1_thiamine") as Double? + val b2Riboflavin = call.argument("b2_riboflavin") as Double? + val b3Niacin = call.argument("b3_niacin") as Double? + val b5PantothenicAcid = call.argument("b5_pantothenic_acid") as Double? + val b6Pyridoxine = call.argument("b6_pyridoxine") as Double? + val b7Biotin = call.argument("b7_biotin") as Double? + val b9Folate = call.argument("b9_folate") as Double? + val b12Cobalamin = call.argument("b12_cobalamin") as Double? + val vitaminC = call.argument("vitamin_c") as Double? + val vitaminD = call.argument("vitamin_d") as Double? + val vitaminE = call.argument("vitamin_e") as Double? + val vitaminK = call.argument("vitamin_k") as Double? + val calcium = call.argument("calcium") as Double? + val chloride = call.argument("chloride") as Double? + val cholesterol = call.argument("cholesterol") as Double? + // Choline is not yet supported by Health Connect + // val choline = call.argument("choline") as Double? + val chromium = call.argument("chromium") as Double? + val copper = call.argument("copper") as Double? + val fatUnsaturated = call.argument("fat_unsaturated") as Double? + val fatMonounsaturated = call.argument("fat_monounsaturated") as Double? + val fatPolyunsaturated = call.argument("fat_polyunsaturated") as Double? + val fatSaturated = call.argument("fat_saturated") as Double? + val fatTransMonoenoic = call.argument("fat_trans_monoenoic") as Double? + val fiber = call.argument("fiber") as Double? + val iodine = call.argument("iodine") as Double? + val iron = call.argument("iron") as Double? + val magnesium = call.argument("magnesium") as Double? + val manganese = call.argument("manganese") as Double? + val molybdenum = call.argument("molybdenum") as Double? + val phosphorus = call.argument("phosphorus") as Double? + val potassium = call.argument("potassium") as Double? + val selenium = call.argument("selenium") as Double? + val sodium = call.argument("sodium") as Double? + val sugar = call.argument("sugar") as Double? + // Water is not support on a food in Health Connect + // val water = call.argument("water") as Double? + val zinc = call.argument("zinc") as Double? + + val name = call.argument("name") + val mealType = call.argument("meal_type")!! + + scope.launch { + try { + val list = mutableListOf() + list.add( + NutritionRecord( + name = name, + energy = calories?.kilocalories, + totalCarbohydrate = carbs?.grams, + protein = protein?.grams, + totalFat = fat?.grams, + caffeine = caffeine?.grams, + vitaminA = vitaminA?.grams, + thiamin = b1Thiamine?.grams, + riboflavin = b2Riboflavin?.grams, + niacin = b3Niacin?.grams, + pantothenicAcid = b5PantothenicAcid?.grams, + vitaminB6 = b6Pyridoxine?.grams, + biotin = b7Biotin?.grams, + folate = b9Folate?.grams, + vitaminB12 = b12Cobalamin?.grams, + vitaminC = vitaminC?.grams, + vitaminD = vitaminD?.grams, + vitaminE = vitaminE?.grams, + vitaminK = vitaminK?.grams, + calcium = calcium?.grams, + chloride = chloride?.grams, + cholesterol = cholesterol?.grams, + chromium = chromium?.grams, + copper = copper?.grams, + unsaturatedFat = fatUnsaturated?.grams, + monounsaturatedFat = fatMonounsaturated?.grams, + polyunsaturatedFat = fatPolyunsaturated?.grams, + saturatedFat = fatSaturated?.grams, + transFat = fatTransMonoenoic?.grams, + dietaryFiber = fiber?.grams, + iodine = iodine?.grams, + iron = iron?.grams, + magnesium = magnesium?.grams, + manganese = manganese?.grams, + molybdenum = molybdenum?.grams, + phosphorus = phosphorus?.grams, + potassium = potassium?.grams, + selenium = selenium?.grams, + sodium = sodium?.grams, + sugar = sugar?.grams, + zinc = zinc?.grams, + startTime = startTime, + startZoneOffset = null, + endTime = endTime, + endZoneOffset = null, + mealType = + mapMealTypeToType[ + mealType] + ?: MEAL_TYPE_UNKNOWN, + ), + ) + healthConnectClient.insertRecords( + list, + ) + result.success(true) + Log.i( + "FLUTTER_HEALTH::SUCCESS", + "[Health Connect] Meal was successfully added!" + ) + } catch (e: Exception) { + Log.w( + "FLUTTER_HEALTH::ERROR", + "[Health Connect] There was an error adding the meal", + ) + Log.w("FLUTTER_HEALTH::ERROR", e.message ?: "unknown error") + Log.w("FLUTTER_HEALTH::ERROR", e.stackTrace.toString()) + result.success(false) + } } - - /** Handle calls from the MethodChannel */ - override fun onMethodCall(call: MethodCall, result: Result) { - when (call.method) { - "installHealthConnect" -> installHealthConnect(call, result) - "useHealthConnectIfAvailable" -> useHealthConnectIfAvailable(call, result) - "getHealthConnectSdkStatus" -> getHealthConnectSdkStatus(call, result) - "hasPermissions" -> hasPermissions(call, result) - "requestAuthorization" -> requestAuthorization(call, result) - "revokePermissions" -> revokePermissions(call, result) - "getData" -> getData(call, result) - "getIntervalData" -> getIntervalData(call, result) - "writeData" -> writeData(call, result) - "delete" -> delete(call, result) - "getAggregateData" -> getAggregateData(call, result) - "getTotalStepsInInterval" -> getTotalStepsInInterval(call, result) - "writeWorkoutData" -> writeWorkoutData(call, result) - "writeBloodPressure" -> writeBloodPressure(call, result) - "writeBloodOxygen" -> writeBloodOxygen(call, result) - "writeMenstruationFlow" -> writeMenstruationFlow(call, result) - "writeMeal" -> writeMeal(call, result) - "disconnect" -> disconnect(call, result) - else -> result.notImplemented() - } + } + + /** + * Save menstrual flow data + */ + private fun writeMenstruationFlow(call: MethodCall, result: Result) { + writeData(call, result) + } + + /** + * Save the blood oxygen saturation + */ + private fun writeBloodOxygen(call: MethodCall, result: Result) { + writeData(call, result) + } + + private fun getIntervalData(call: MethodCall, result: Result) { + getAggregateData(call, result) + } + + /** + * Revokes access to Health Connect using `revokeAllPermissions`. + * + * Note: When using `revokePermissions` with Health Connect, the app must be completely killed + * for it to take effect. + */ + private fun revokePermissions(call: MethodCall, result: Result) { + scope.launch { + Log.i("Health", "Disabling Health Connect") + healthConnectClient.permissionController.revokeAllPermissions() } + result.success(true) + } + + private fun getTotalStepsInInterval(call: MethodCall, result: Result) { + val start = call.argument("startTime")!! + val end = call.argument("endTime")!! + + scope.launch { + try { + val startInstant = Instant.ofEpochMilli(start) + val endInstant = Instant.ofEpochMilli(end) + val response = + healthConnectClient.aggregate( + AggregateRequest( + metrics = + setOf( + StepsRecord.COUNT_TOTAL + ), + timeRangeFilter = + TimeRangeFilter.between( + startInstant, + endInstant + ), + ), + ) + // The result may be null if no data is available in the + // time range. + val stepsInInterval = + response[StepsRecord.COUNT_TOTAL] ?: 0L + Log.i( + "FLUTTER_HEALTH::SUCCESS", + "returning $stepsInInterval steps" + ) + result.success(stepsInInterval) + } catch (e: Exception) { + Log.e( + "FLUTTER_HEALTH::ERROR", + "Unable to return steps due to the following exception:" + ) + Log.e("FLUTTER_HEALTH::ERROR", Log.getStackTraceString(e)) + result.success(null) + } + } + } - override fun onAttachedToActivity(binding: ActivityPluginBinding) { - if (channel == null) { - return - } - binding.addActivityResultListener(this) - activity = binding.activity - - val requestPermissionActivityContract = - PermissionController.createRequestPermissionResultContract() - healthConnectRequestPermissionsLauncher = - (activity as ComponentActivity).registerForActivityResult( - requestPermissionActivityContract - ) { granted -> onHealthConnectPermissionCallback(granted) } + private fun getHealthConnectSdkStatus(call: MethodCall, result: Result) { + checkAvailability() + if (healthConnectAvailable) { + healthConnectClient = + HealthConnectClient.getOrCreate( + context!! + ) } - - override fun onDetachedFromActivityForConfigChanges() { - onDetachedFromActivity() + result.success(healthConnectStatus) + } + + private fun hasPermissions(call: MethodCall, result: Result) { + val args = call.arguments as HashMap<*, *> + val types = (args["types"] as? ArrayList<*>)?.filterIsInstance()!! + val permissions = (args["permissions"] as? ArrayList<*>)?.filterIsInstance()!! + + val permList = mutableListOf() + for ((i, typeKey) in types.withIndex()) { + if (!mapToType.containsKey(typeKey)) { + Log.w( + "FLUTTER_HEALTH::ERROR", + "Datatype $typeKey not found in HC" + ) + result.success(false) + return + } + val access = permissions[i] + val dataType = mapToType[typeKey]!! + if (access == 0) { + permList.add( + HealthPermission.getReadPermission(dataType), + ) + } else { + permList.addAll( + listOf( + HealthPermission.getReadPermission( + dataType + ), + HealthPermission.getWritePermission( + dataType + ), + ), + ) + } + // 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 + ), + ), + ) + } + } } - - override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { - onAttachedToActivity(binding) + scope.launch { + result.success( + healthConnectClient + .permissionController + .getGrantedPermissions() + .containsAll(permList), + ) } - - override fun onDetachedFromActivity() { - if (channel == null) { - return - } - activity = null - healthConnectRequestPermissionsLauncher = null + } + + /** + * Requests authorization for the HealthDataTypes with the the READ or READ_WRITE permission + * type. + */ + private fun requestAuthorization(call: MethodCall, result: Result) { + if (context == null) { + result.success(false) + return } - /** HEALTH CONNECT BELOW */ - var healthConnectAvailable = false - var healthConnectStatus = HealthConnectClient.SDK_UNAVAILABLE + val args = call.arguments as HashMap<*, *> + val types = (args["types"] as? ArrayList<*>)?.filterIsInstance()!! + val permissions = (args["permissions"] as? ArrayList<*>)?.filterIsInstance()!! - fun checkAvailability() { - healthConnectStatus = HealthConnectClient.getSdkStatus(context!!) - healthConnectAvailable = healthConnectStatus == HealthConnectClient.SDK_AVAILABLE - } - - private fun installHealthConnect(call: MethodCall, result: Result) { - val uriString = - "market://details?id=com.google.android.apps.healthdata&url=healthconnect%3A%2F%2Fonboarding" - context!!.startActivity( - Intent(Intent.ACTION_VIEW).apply { - setPackage("com.android.vending") - data = Uri.parse(uriString) - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - putExtra("overlay", true) - putExtra("callerId", context!!.packageName) - } + val permList = mutableListOf() + for ((i, typeKey) in types.withIndex()) { + if (!mapToType.containsKey(typeKey)) { + Log.w( + "FLUTTER_HEALTH::ERROR", + "Datatype $typeKey not found in HC" ) - result.success(null) + result.success(false) + return + } + val access = permissions[i]!! + val dataType = mapToType[typeKey]!! + if (access == 0) { + permList.add( + HealthPermission.getReadPermission(dataType), + ) + } else { + permList.addAll( + listOf( + HealthPermission.getReadPermission( + dataType + ), + HealthPermission.getWritePermission( + dataType + ), + ), + ) + } + // 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 + ), + ), + ) + } + } } - - fun useHealthConnectIfAvailable(call: MethodCall, result: Result) { - useHealthConnectIfAvailable = true - result.success(null) + if (healthConnectRequestPermissionsLauncher == null) { + result.success(false) + Log.i("FLUTTER_HEALTH", "Permission launcher not found") + return } - private fun getHealthConnectSdkStatus(call: MethodCall, result: Result) { - checkAvailability() - if (healthConnectAvailable) { - healthConnectClient = - HealthConnectClient.getOrCreate( - context!! + // Store the result to be called in [onHealthConnectPermissionCallback] + mResult = result + healthConnectRequestPermissionsLauncher!!.launch(permList.toSet()) + } + + /** Get all datapoints of the DataType within the given time range */ + private fun getData(call: MethodCall, result: Result) { + val dataType = call.argument("dataTypeKey")!! + val startTime = Instant.ofEpochMilli(call.argument("startTime")!!) + val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) + val healthConnectData = mutableListOf>() + scope.launch { + try { + mapToType[dataType]?.let { classType -> + val records = mutableListOf() + + // Set up the initial request to read health records with specified + // parameters + var request = + ReadRecordsRequest( + recordType = classType, + // Define the maximum amount of data + // that HealthConnect can return + // in a single request + timeRangeFilter = + TimeRangeFilter.between( + startTime, + endTime + ), ) - } - result.success(healthConnectStatus) - } - private fun hasPermissionsHC(call: MethodCall, result: Result) { - val args = call.arguments as HashMap<*, *> - val types = (args["types"] as? ArrayList<*>)?.filterIsInstance()!! - val permissions = (args["permissions"] as? ArrayList<*>)?.filterIsInstance()!! + var response = healthConnectClient.readRecords(request) + var pageToken = response.pageToken + + // Add the records from the initial response to the records list + records.addAll(response.records) + + // Continue making requests and fetching records while there is a + // page token + while (!pageToken.isNullOrEmpty()) { + request = + ReadRecordsRequest( + recordType = classType, + timeRangeFilter = + TimeRangeFilter.between( + startTime, + endTime + ), + pageToken = pageToken + ) + response = healthConnectClient.readRecords(request) + + pageToken = response.pageToken + records.addAll(response.records) + } - var permList = mutableListOf() - for ((i, typeKey) in types.withIndex()) { - if (!MapToHCType.containsKey(typeKey)) { - Log.w( - "FLUTTER_HEALTH::ERROR", - "Datatype " + typeKey + " not found in HC" + // Workout needs distance and total calories burned too + if (dataType == WORKOUT) { + for (rec in records) { + val record = rec as ExerciseSessionRecord + val distanceRequest = + healthConnectClient.readRecords( + ReadRecordsRequest( + recordType = + DistanceRecord::class, + timeRangeFilter = + TimeRangeFilter.between( + record.startTime, + record.endTime, + ), + ), ) - result.success(false) - return - } - val access = permissions[i] - val dataType = MapToHCType[typeKey]!! - if (access == 0) { - permList.add( - HealthPermission.getReadPermission(dataType), + var totalDistance = 0.0 + for (distanceRec in distanceRequest.records) { + totalDistance += + distanceRec.distance + .inMeters + } + + val energyBurnedRequest = + healthConnectClient.readRecords( + ReadRecordsRequest( + recordType = + TotalCaloriesBurnedRecord::class, + timeRangeFilter = + TimeRangeFilter.between( + record.startTime, + record.endTime, + ), + ), ) - } else { - permList.addAll( - listOf( - HealthPermission.getReadPermission( - dataType - ), - HealthPermission.getWritePermission( - dataType - ), - ), + var totalEnergyBurned = 0.0 + for (energyBurnedRec in + energyBurnedRequest.records) { + totalEnergyBurned += + energyBurnedRec.energy + .inKilocalories + } + + val stepRequest = + healthConnectClient.readRecords( + ReadRecordsRequest( + recordType = + StepsRecord::class, + timeRangeFilter = + TimeRangeFilter.between( + record.startTime, + record.endTime + ), + ), ) + var totalSteps = 0.0 + for (stepRec in stepRequest.records) { + totalSteps += stepRec.count + } + + // val metadata = (rec as Record).metadata + // Add final datapoint + healthConnectData.add( + // mapOf( + mapOf( + "workoutActivityType" to + (workoutTypeMap + .filterValues { + it == + record.exerciseType + } + .keys + .firstOrNull() + ?: "OTHER"), + "totalDistance" to + if (totalDistance == + 0.0 + ) + null + else + totalDistance, + "totalDistanceUnit" to + "METER", + "totalEnergyBurned" to + if (totalEnergyBurned == + 0.0 + ) + null + else + totalEnergyBurned, + "totalEnergyBurnedUnit" to + "KILOCALORIE", + "totalSteps" to + if (totalSteps == + 0.0 + ) + null + else + totalSteps, + "totalStepsUnit" to + "COUNT", + "unit" to "MINUTES", + "date_from" to + rec.startTime + .toEpochMilli(), + "date_to" to + rec.endTime.toEpochMilli(), + "source_id" to "", + "source_name" to + record.metadata + .dataOrigin + .packageName, + ), + ) } - // 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 - ), - ), + // Filter sleep stages for requested stage + } else if (classType == SleepSessionRecord::class) { + for (rec in response.records) { + if (rec is SleepSessionRecord) { + if (dataType == SLEEP_SESSION) { + healthConnectData.addAll( + convertRecord( + rec, + dataType ) + ) } else { - permList.addAll( - listOf( - HealthPermission.getReadPermission( - DistanceRecord::class - ), - HealthPermission.getReadPermission( - TotalCaloriesBurnedRecord::class - ), - HealthPermission.getWritePermission( - DistanceRecord::class - ), - HealthPermission.getWritePermission( - TotalCaloriesBurnedRecord::class - ), - ), - ) + for (recStage in rec.stages) { + if (dataType == + mapSleepStageToType[ + recStage.stage] + ) { + healthConnectData + .addAll( + convertRecordStage( + recStage, + dataType, + rec.metadata.dataOrigin + .packageName + ) + ) + } + } } + } } + } else { + for (rec in records) { + healthConnectData.addAll( + convertRecord(rec, dataType) + ) + } + } } - scope.launch { - result.success( - healthConnectClient - .permissionController - .getGrantedPermissions() - .containsAll(permList), - ) - } + Handler(context!!.mainLooper).run { result.success(healthConnectData) } + } catch (e: Exception) { + Log.i( + "FLUTTER_HEALTH::ERROR", + "Unable to return $dataType due to the following exception:" + ) + Log.e("FLUTTER_HEALTH::ERROR", Log.getStackTraceString(e)) + result.success(null) + } } + } + + private fun convertRecordStage( + stage: SleepSessionRecord.Stage, + dataType: String, + sourceName: String + ): List> { + return listOf( + mapOf( + "stage" to stage.stage, + "value" to + ChronoUnit.MINUTES.between( + stage.startTime, + stage.endTime + ), + "date_from" to stage.startTime.toEpochMilli(), + "date_to" to stage.endTime.toEpochMilli(), + "source_id" to "", + "source_name" to sourceName, + ), + ) + } + + private fun getAggregateData(call: MethodCall, result: Result) { + val dataType = call.argument("dataTypeKey")!! + val interval = call.argument("interval")!! + val startTime = Instant.ofEpochMilli(call.argument("startTime")!!) + val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) + val healthConnectData = mutableListOf>() + scope.launch { + try { + mapToAggregateMetric[dataType]?.let { metricClassType -> + val request = + AggregateGroupByDurationRequest( + metrics = setOf(metricClassType), + timeRangeFilter = + TimeRangeFilter.between( + startTime, + endTime + ), + timeRangeSlicer = + Duration.ofSeconds( + interval + ) + ) + val response = healthConnectClient.aggregateGroupByDuration(request) + + for (durationResult in response) { + // The result may be null if no data is available in the + // time range + var totalValue = durationResult.result[metricClassType] + if (totalValue is Length) { + totalValue = totalValue.inMeters + } else if (totalValue is Energy) { + totalValue = totalValue.inKilocalories + } - private fun requestAuthorizationHC(call: MethodCall, result: Result) { - val args = call.arguments as HashMap<*, *> - val types = (args["types"] as? ArrayList<*>)?.filterIsInstance()!! - val permissions = (args["permissions"] as? ArrayList<*>)?.filterIsInstance()!! + val packageNames = + durationResult.result.dataOrigins + .joinToString { origin -> + origin.packageName + } - var permList = mutableListOf() - for ((i, typeKey) in types.withIndex()) { - if (!MapToHCType.containsKey(typeKey)) { - Log.w( - "FLUTTER_HEALTH::ERROR", - "Datatype " + typeKey + " not found in HC" - ) - result.success(false) - return - } - val access = permissions[i]!! - val dataType = MapToHCType[typeKey]!! - if (access == 0) { - permList.add( - HealthPermission.getReadPermission(dataType), - ) - } else { - permList.addAll( - listOf( - HealthPermission.getReadPermission( - dataType - ), - HealthPermission.getWritePermission( - dataType - ), - ), - ) - } - // 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 - ), - ), + val data = + mapOf( + "value" to + (totalValue + ?: 0), + "date_from" to + durationResult.startTime + .toEpochMilli(), + "date_to" to + durationResult.endTime + .toEpochMilli(), + "source_name" to + packageNames, + "source_id" to "", + "is_manual_entry" to + packageNames.contains( + "user_input" ) - } - } - } - if (healthConnectRequestPermissionsLauncher == null) { - result.success(false) - Log.i("FLUTTER_HEALTH", "Permission launcher not found") - return + ) + healthConnectData.add(data) + } } - - healthConnectRequestPermissionsLauncher!!.launch(permList.toSet()) + Handler(context!!.mainLooper).run { result.success(healthConnectData) } + } catch (e: Exception) { + Log.i( + "FLUTTER_HEALTH::ERROR", + "Unable to return $dataType due to the following exception:" + ) + Log.e("FLUTTER_HEALTH::ERROR", Log.getStackTraceString(e)) + result.success(null) + } } + } - fun getHCData(call: MethodCall, result: Result) { - val dataType = call.argument("dataTypeKey")!! - val startTime = Instant.ofEpochMilli(call.argument("startTime")!!) - val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) - val healthConnectData = mutableListOf>() - scope.launch { - try { - MapToHCType[dataType]?.let { classType -> - val records = mutableListOf() + // TODO: Find alternative to SOURCE_ID or make it nullable? + private fun convertRecord(record: Any, dataType: String): List> { + val metadata = (record as Record).metadata + when (record) { + is WeightRecord -> + return listOf( + mapOf( + "value" to + record.weight + .inKilograms, + "date_from" to + record.time + .toEpochMilli(), + "date_to" to + record.time + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - // Set up the initial request to read health records with specified - // parameters - var request = - ReadRecordsRequest( - recordType = classType, - // Define the maximum amount of data - // that HealthConnect can return - // in a single request - timeRangeFilter = - TimeRangeFilter.between( - startTime, - endTime - ), - ) + is HeightRecord -> + return listOf( + mapOf( + "value" to + record.height + .inMeters, + "date_from" to + record.time + .toEpochMilli(), + "date_to" to + record.time + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - var response = healthConnectClient.readRecords(request) - var pageToken = response.pageToken + is BodyFatRecord -> + return listOf( + mapOf( + "value" to + record.percentage + .value, + "date_from" to + record.time + .toEpochMilli(), + "date_to" to + record.time + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - // Add the records from the initial response to the records list - records.addAll(response.records) + is StepsRecord -> + return listOf( + mapOf( + "value" to record.count, + "date_from" to + record.startTime + .toEpochMilli(), + "date_to" to + record.endTime + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - // Continue making requests and fetching records while there is a - // page token - while (!pageToken.isNullOrEmpty()) { - request = - ReadRecordsRequest( - recordType = classType, - timeRangeFilter = - TimeRangeFilter.between( - startTime, - endTime - ), - pageToken = pageToken - ) - response = healthConnectClient.readRecords(request) + is ActiveCaloriesBurnedRecord -> + return listOf( + mapOf( + "value" to + record.energy + .inKilocalories, + "date_from" to + record.startTime + .toEpochMilli(), + "date_to" to + record.endTime + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - pageToken = response.pageToken - records.addAll(response.records) - } + is HeartRateRecord -> + return record.samples.map { + mapOf( + "value" to it.beatsPerMinute, + "date_from" to + it.time.toEpochMilli(), + "date_to" to it.time.toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ) + } + + is HeartRateVariabilityRmssdRecord -> + return listOf( + mapOf( + "value" to + record.heartRateVariabilityMillis, + "date_from" to + record.time + .toEpochMilli(), + "date_to" to + record.time + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - // Workout needs distance and total calories burned too - if (dataType == WORKOUT) { - for (rec in records) { - val record = rec as ExerciseSessionRecord - val distanceRequest = - healthConnectClient.readRecords( - ReadRecordsRequest( - recordType = - DistanceRecord::class, - timeRangeFilter = - TimeRangeFilter.between( - record.startTime, - record.endTime, - ), - ), - ) - var totalDistance = 0.0 - for (distanceRec in distanceRequest.records) { - totalDistance += - distanceRec.distance - .inMeters - } + is BodyTemperatureRecord -> + return listOf( + mapOf( + "value" to + record.temperature + .inCelsius, + "date_from" to + record.time + .toEpochMilli(), + "date_to" to + record.time + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - val energyBurnedRequest = - healthConnectClient.readRecords( - ReadRecordsRequest( - recordType = - TotalCaloriesBurnedRecord::class, - timeRangeFilter = - TimeRangeFilter.between( - record.startTime, - record.endTime, - ), - ), - ) - var totalEnergyBurned = 0.0 - for (energyBurnedRec in - energyBurnedRequest.records) { - totalEnergyBurned += - energyBurnedRec.energy - .inKilocalories - } + is BodyWaterMassRecord -> + return listOf( + mapOf( + "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, + ), + ) - val stepRequest = - healthConnectClient.readRecords( - ReadRecordsRequest( - recordType = - StepsRecord::class, - timeRangeFilter = - TimeRangeFilter.between( - record.startTime, - record.endTime - ), - ), - ) - var totalSteps = 0.0 - for (stepRec in stepRequest.records) { - totalSteps += stepRec.count - } + is BloodPressureRecord -> + return listOf( + mapOf( + "value" to + if (dataType == + BLOOD_PRESSURE_DIASTOLIC + ) + record.diastolic + .inMillimetersOfMercury + else + record.systolic + .inMillimetersOfMercury, + "date_from" to + record.time + .toEpochMilli(), + "date_to" to + record.time + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - // val metadata = (rec as Record).metadata - // Add final datapoint - healthConnectData.add( - // mapOf( - mapOf( - "workoutActivityType" to - (workoutTypeMapHealthConnect - .filterValues { - it == - record.exerciseType - } - .keys - .firstOrNull() - ?: "OTHER"), - "totalDistance" to - if (totalDistance == - 0.0 - ) - null - else - totalDistance, - "totalDistanceUnit" to - "METER", - "totalEnergyBurned" to - if (totalEnergyBurned == - 0.0 - ) - null - else - totalEnergyBurned, - "totalEnergyBurnedUnit" to - "KILOCALORIE", - "totalSteps" to - if (totalSteps == - 0.0 - ) - null - else - totalSteps, - "totalStepsUnit" to - "COUNT", - "unit" to "MINUTES", - "date_from" to - rec.startTime - .toEpochMilli(), - "date_to" to - rec.endTime.toEpochMilli(), - "source_id" to "", - "source_name" to - record.metadata - .dataOrigin - .packageName, - ), - ) - } - // Filter sleep stages for requested stage - } else if (classType == SleepSessionRecord::class) { - for (rec in response.records) { - if (rec is SleepSessionRecord) { - if (dataType == SLEEP_SESSION) { - healthConnectData.addAll( - convertRecord( - rec, - dataType - ) - ) - } else { - for (recStage in rec.stages) { - if (dataType == - MapSleepStageToType[ - recStage.stage] - ) { - healthConnectData - .addAll( - convertRecordStage( - recStage, - dataType, - rec.metadata.dataOrigin - .packageName - ) - ) - } - } - } - } - } - } else { - for (rec in records) { - healthConnectData.addAll( - convertRecord(rec, dataType) - ) - } - } - } - Handler(context!!.mainLooper).run { result.success(healthConnectData) } - } catch (e: Exception) { - Log.i("FLUTTER_HEALTH::ERROR", "Unable to return $dataType due to the following exception:") - Log.e("FLUTTER_HEALTH::ERROR", Log.getStackTraceString(e)) - result.success(null) - } - } - } + is OxygenSaturationRecord -> + return listOf( + mapOf( + "value" to + record.percentage + .value, + "date_from" to + record.time + .toEpochMilli(), + "date_to" to + record.time + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - fun convertRecordStage( - stage: SleepSessionRecord.Stage, - dataType: String, - sourceName: String - ): List> { + is BloodGlucoseRecord -> return listOf( - mapOf( - "stage" to stage.stage, - "value" to - ChronoUnit.MINUTES.between( - stage.startTime, - stage.endTime - ), - "date_from" to stage.startTime.toEpochMilli(), - "date_to" to stage.endTime.toEpochMilli(), - "source_id" to "", - "source_name" to sourceName, - ), + mapOf( + "value" to + record.level + .inMilligramsPerDeciliter, + "date_from" to + record.time + .toEpochMilli(), + "date_to" to + record.time + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), ) - } - fun getAggregateHCData(call: MethodCall, result: Result) { - val dataType = call.argument("dataTypeKey")!! - val interval = call.argument("interval")!! - val startTime = Instant.ofEpochMilli(call.argument("startTime")!!) - val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) - val healthConnectData = mutableListOf>() - scope.launch { - try { - MapToHCAggregateMetric[dataType]?.let { metricClassType -> - val request = - AggregateGroupByDurationRequest( - metrics = setOf(metricClassType), - timeRangeFilter = - TimeRangeFilter.between( - startTime, - endTime - ), - timeRangeSlicer = - Duration.ofSeconds( - interval - ) - ) - val response = healthConnectClient.aggregateGroupByDuration(request) + is DistanceRecord -> + return listOf( + mapOf( + "value" to + record.distance + .inMeters, + "date_from" to + record.startTime + .toEpochMilli(), + "date_to" to + record.endTime + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - for (durationResult in response) { - // The result may be null if no data is available in the - // time range - var totalValue = durationResult.result[metricClassType] - if (totalValue is Length) { - totalValue = totalValue.inMeters - } else if (totalValue is Energy) { - totalValue = totalValue.inKilocalories - } + is HydrationRecord -> + return listOf( + mapOf( + "value" to + record.volume + .inLiters, + "date_from" to + record.startTime + .toEpochMilli(), + "date_to" to + record.endTime + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - val packageNames = - durationResult.result.dataOrigins - .joinToString { origin -> - "${origin.packageName}" - } + is TotalCaloriesBurnedRecord -> + return listOf( + mapOf( + "value" to + record.energy + .inKilocalories, + "date_from" to + record.startTime + .toEpochMilli(), + "date_to" to + record.endTime + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - val data = - mapOf( - "value" to - (totalValue - ?: 0), - "date_from" to - durationResult.startTime - .toEpochMilli(), - "date_to" to - durationResult.endTime - .toEpochMilli(), - "source_name" to - packageNames, - "source_id" to "", - "is_manual_entry" to - packageNames.contains( - "user_input" - ) - ) - healthConnectData.add(data) - } - } - Handler(context!!.mainLooper).run { result.success(healthConnectData) } - } catch (e: Exception) { - Log.i("FLUTTER_HEALTH::ERROR", "Unable to return $dataType due to the following exception:") - Log.e("FLUTTER_HEALTH::ERROR", Log.getStackTraceString(e)) - result.success(null) - } - } - } + is BasalMetabolicRateRecord -> + return listOf( + mapOf( + "value" to + record.basalMetabolicRate + .inKilocaloriesPerDay, + "date_from" to + record.time + .toEpochMilli(), + "date_to" to + record.time + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - // TODO: Find alternative to SOURCE_ID or make it nullable? - fun convertRecord(record: Any, dataType: String): List> { - val metadata = (record as Record).metadata - when (record) { - is WeightRecord -> - return listOf( - mapOf( - "value" to - record.weight - .inKilograms, - "date_from" to - record.time - .toEpochMilli(), - "date_to" to - record.time - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is HeightRecord -> - return listOf( - mapOf( - "value" to - record.height - .inMeters, - "date_from" to - record.time - .toEpochMilli(), - "date_to" to - record.time - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is BodyFatRecord -> - return listOf( - mapOf( - "value" to - record.percentage - .value, - "date_from" to - record.time - .toEpochMilli(), - "date_to" to - record.time - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is StepsRecord -> - return listOf( - mapOf( - "value" to record.count, - "date_from" to - record.startTime - .toEpochMilli(), - "date_to" to - record.endTime - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is ActiveCaloriesBurnedRecord -> - return listOf( - mapOf( - "value" to - record.energy - .inKilocalories, - "date_from" to - record.startTime - .toEpochMilli(), - "date_to" to - record.endTime - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is HeartRateRecord -> - return record.samples.map { - mapOf( - "value" to it.beatsPerMinute, - "date_from" to - it.time.toEpochMilli(), - "date_to" to it.time.toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ) - } - is HeartRateVariabilityRmssdRecord -> - return listOf( - mapOf( - "value" to - record.heartRateVariabilityMillis, - "date_from" to - record.time - .toEpochMilli(), - "date_to" to - record.time - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is BodyTemperatureRecord -> - return listOf( - mapOf( - "value" to - record.temperature - .inCelsius, - "date_from" to - record.time - .toEpochMilli(), - "date_to" to - record.time - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is BodyWaterMassRecord -> - return listOf( - mapOf( - "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, - ), - ) - is BloodPressureRecord -> - return listOf( - mapOf( - "value" to - if (dataType == - BLOOD_PRESSURE_DIASTOLIC - ) - record.diastolic - .inMillimetersOfMercury - else - record.systolic - .inMillimetersOfMercury, - "date_from" to - record.time - .toEpochMilli(), - "date_to" to - record.time - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is OxygenSaturationRecord -> - return listOf( - mapOf( - "value" to - record.percentage - .value, - "date_from" to - record.time - .toEpochMilli(), - "date_to" to - record.time - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is BloodGlucoseRecord -> - return listOf( - mapOf( - "value" to - record.level - .inMilligramsPerDeciliter, - "date_from" to - record.time - .toEpochMilli(), - "date_to" to - record.time - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is DistanceRecord -> - return listOf( - mapOf( - "value" to - record.distance - .inMeters, - "date_from" to - record.startTime - .toEpochMilli(), - "date_to" to - record.endTime - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is HydrationRecord -> - return listOf( - mapOf( - "value" to - record.volume - .inLiters, - "date_from" to - record.startTime - .toEpochMilli(), - "date_to" to - record.endTime - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is TotalCaloriesBurnedRecord -> - return listOf( - mapOf( - "value" to - record.energy - .inKilocalories, - "date_from" to - record.startTime - .toEpochMilli(), - "date_to" to - record.endTime - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is BasalMetabolicRateRecord -> - return listOf( - mapOf( - "value" to - record.basalMetabolicRate - .inKilocaloriesPerDay, - "date_from" to - record.time - .toEpochMilli(), - "date_to" to - record.time - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is SleepSessionRecord -> - return listOf( - mapOf( - "date_from" to - record.startTime - .toEpochMilli(), - "date_to" to - record.endTime - .toEpochMilli(), - "value" to - ChronoUnit.MINUTES - .between( - record.startTime, - record.endTime - ), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ), - ) - is RestingHeartRateRecord -> - return listOf( - mapOf( - "value" to - record.beatsPerMinute, - "date_from" to - record.time - .toEpochMilli(), - "date_to" to - record.time - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ) - ) - is BasalMetabolicRateRecord -> - return listOf( - mapOf( - "value" to - record.basalMetabolicRate - .inKilocaloriesPerDay, - "date_from" to - record.time - .toEpochMilli(), - "date_to" to - record.time - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ) - ) - is FloorsClimbedRecord -> - return listOf( - mapOf( - "value" to record.floors, - "date_from" to - record.startTime - .toEpochMilli(), - "date_to" to - record.endTime - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ) - ) - is RespiratoryRateRecord -> - return listOf( - mapOf( - "value" to record.rate, - "date_from" to - record.time - .toEpochMilli(), - "date_to" to - record.time - .toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ) - ) - is NutritionRecord -> - return listOf( - mapOf( - "calories" to record.energy?.inKilocalories, - "protein" to record.protein?.inGrams, - "carbs" to record.totalCarbohydrate?.inGrams, - "fat" to record.totalFat?.inGrams, - "caffeine" to record.caffeine?.inGrams, - "vitamin_a" to record.vitaminA?.inGrams, - "b1_thiamine" to record.thiamin?.inGrams, - "b2_riboflavin" to record.riboflavin?.inGrams, - "b3_niacin" to record.niacin?.inGrams, - "b5_pantothenic_acid" to record.pantothenicAcid?.inGrams, - "b6_pyridoxine" to record.vitaminB6?.inGrams, - "b7_biotin" to record.biotin?.inGrams, - "b9_folate" to record.folate?.inGrams, - "b12_cobalamin" to record.vitaminB12?.inGrams, - "vitamin_c" to record.vitaminC?.inGrams, - "vitamin_d" to record.vitaminD?.inGrams, - "vitamin_e" to record.vitaminE?.inGrams, - "vitamin_k" to record.vitaminK?.inGrams, - "calcium" to record.calcium?.inGrams, - "chloride" to record.chloride?.inGrams, - "cholesterol" to record.cholesterol?.inGrams, - "choline" to null, - "chromium" to record.chromium?.inGrams, - "copper" to record.copper?.inGrams, - "fat_unsaturated" to record.unsaturatedFat?.inGrams, - "fat_monounsaturated" to record.monounsaturatedFat?.inGrams, - "fat_polyunsaturated" to record.polyunsaturatedFat?.inGrams, - "fat_saturated" to record.saturatedFat?.inGrams, - "fat_trans_monoenoic" to record.transFat?.inGrams, - "fiber" to record.dietaryFiber?.inGrams, - "iodine" to record.iodine?.inGrams, - "iron" to record.iron?.inGrams, - "magnesium" to record.magnesium?.inGrams, - "manganese" to record.manganese?.inGrams, - "molybdenum" to record.molybdenum?.inGrams, - "phosphorus" to record.phosphorus?.inGrams, - "potassium" to record.potassium?.inGrams, - "selenium" to record.selenium?.inGrams, - "sodium" to record.sodium?.inGrams, - "sugar" to record.sugar?.inGrams, - "water" to null, - "zinc" to record.zinc?.inGrams, - "name" to record.name!!, - "meal_type" 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, - ) - ) - is MenstruationFlowRecord -> - return listOf( - mapOf( - "value" to record.flow, - "date_from" to record.time.toEpochMilli(), - "date_to" to record.time.toEpochMilli(), - "source_id" to "", - "source_name" to - metadata.dataOrigin - .packageName, - ) - ) - // is ExerciseSessionRecord -> return listOf(mapOf("value" to , - // "date_from" to , - // "date_to" to , - // "source_id" to "", - // "source_name" to - // metadata.dataOrigin.packageName)) - else -> - throw IllegalArgumentException( - "Health data type not supported" - ) // TODO: Exception or error? - } - } + is SleepSessionRecord -> + return listOf( + mapOf( + "date_from" to + record.startTime + .toEpochMilli(), + "date_to" to + record.endTime + .toEpochMilli(), + "value" to + ChronoUnit.MINUTES + .between( + record.startTime, + record.endTime + ), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ), + ) - // TODO rewrite sleep to fit new update better --> compare with Apple and see if we should - // not - // adopt a single type with attached stages approach - fun writeHCData(call: MethodCall, result: Result) { - val type = call.argument("dataTypeKey")!! - val startTime = call.argument("startTime")!! - val endTime = call.argument("endTime")!! - val value = call.argument("value")!! - val record = - when (type) { - BODY_FAT_PERCENTAGE -> - BodyFatRecord( - time = - Instant.ofEpochMilli( - startTime - ), - percentage = - Percentage( - value - ), - zoneOffset = null, - ) - HEIGHT -> - HeightRecord( - time = - Instant.ofEpochMilli( - startTime - ), - height = - Length.meters( - value - ), - zoneOffset = null, - ) - WEIGHT -> - WeightRecord( - time = - Instant.ofEpochMilli( - startTime - ), - weight = - Mass.kilograms( - value - ), - zoneOffset = null, - ) - STEPS -> - StepsRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - count = value.toLong(), - startZoneOffset = null, - endZoneOffset = null, - ) - ACTIVE_ENERGY_BURNED -> - ActiveCaloriesBurnedRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - energy = - Energy.kilocalories( - value - ), - startZoneOffset = null, - endZoneOffset = null, - ) - HEART_RATE -> - HeartRateRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - samples = - listOf< - HeartRateRecord.Sample>( - HeartRateRecord.Sample( - time = - Instant.ofEpochMilli( - startTime - ), - beatsPerMinute = - value.toLong(), - ), - ), - startZoneOffset = null, - endZoneOffset = null, - ) - BODY_TEMPERATURE -> - BodyTemperatureRecord( - time = - Instant.ofEpochMilli( - startTime - ), - temperature = - Temperature.celsius( - value - ), - zoneOffset = null, - ) - BODY_WATER_MASS -> - BodyWaterMassRecord( - time = - Instant.ofEpochMilli( - startTime - ), - mass = - Mass.kilograms( - value - ), - zoneOffset = null, - ) - BLOOD_OXYGEN -> - OxygenSaturationRecord( - time = - Instant.ofEpochMilli( - startTime - ), - percentage = - Percentage( - value - ), - zoneOffset = null, - ) - BLOOD_GLUCOSE -> - BloodGlucoseRecord( - time = - Instant.ofEpochMilli( - startTime - ), - level = - BloodGlucose.milligramsPerDeciliter( - value - ), - zoneOffset = null, - ) - HEART_RATE_VARIABILITY_RMSSD -> - HeartRateVariabilityRmssdRecord( - time = - Instant.ofEpochMilli( - startTime - ), - heartRateVariabilityMillis = - value, - - zoneOffset = null, - ) - DISTANCE_DELTA -> - DistanceRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - distance = - Length.meters( - value - ), - startZoneOffset = null, - endZoneOffset = null, - ) - WATER -> - HydrationRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - volume = - Volume.liters( - value - ), - startZoneOffset = null, - endZoneOffset = null, - ) - SLEEP_ASLEEP -> - SleepSessionRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - startZoneOffset = null, - endZoneOffset = null, - stages = - listOf( - SleepSessionRecord - .Stage( - Instant.ofEpochMilli( - startTime - ), - Instant.ofEpochMilli( - endTime - ), - SleepSessionRecord - .STAGE_TYPE_SLEEPING - ) - ), - ) - SLEEP_LIGHT -> - SleepSessionRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - startZoneOffset = null, - endZoneOffset = null, - stages = - listOf( - SleepSessionRecord - .Stage( - Instant.ofEpochMilli( - startTime - ), - Instant.ofEpochMilli( - endTime - ), - SleepSessionRecord - .STAGE_TYPE_LIGHT - ) - ), - ) - SLEEP_DEEP -> - SleepSessionRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - startZoneOffset = null, - endZoneOffset = null, - stages = - listOf( - SleepSessionRecord - .Stage( - Instant.ofEpochMilli( - startTime - ), - Instant.ofEpochMilli( - endTime - ), - SleepSessionRecord - .STAGE_TYPE_DEEP - ) - ), - ) - SLEEP_REM -> - SleepSessionRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - startZoneOffset = null, - endZoneOffset = null, - stages = - listOf( - SleepSessionRecord - .Stage( - Instant.ofEpochMilli( - startTime - ), - Instant.ofEpochMilli( - endTime - ), - SleepSessionRecord - .STAGE_TYPE_REM - ) - ), - ) - SLEEP_OUT_OF_BED -> - SleepSessionRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - startZoneOffset = null, - endZoneOffset = null, - stages = - listOf( - SleepSessionRecord - .Stage( - Instant.ofEpochMilli( - startTime - ), - Instant.ofEpochMilli( - endTime - ), - SleepSessionRecord - .STAGE_TYPE_OUT_OF_BED - ) - ), - ) - SLEEP_AWAKE -> - SleepSessionRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - startZoneOffset = null, - endZoneOffset = null, - stages = - listOf( - SleepSessionRecord - .Stage( - Instant.ofEpochMilli( - startTime - ), - Instant.ofEpochMilli( - endTime - ), - SleepSessionRecord - .STAGE_TYPE_AWAKE - ) - ), - ) - SLEEP_SESSION -> - SleepSessionRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - startZoneOffset = null, - endZoneOffset = null, - ) - RESTING_HEART_RATE -> - RestingHeartRateRecord( - time = - Instant.ofEpochMilli( - startTime - ), - beatsPerMinute = - value.toLong(), - zoneOffset = null, - ) - BASAL_ENERGY_BURNED -> - BasalMetabolicRateRecord( - time = - Instant.ofEpochMilli( - startTime - ), - basalMetabolicRate = - Power.kilocaloriesPerDay( - value - ), - zoneOffset = null, - ) - FLIGHTS_CLIMBED -> - FloorsClimbedRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - floors = value, - startZoneOffset = null, - endZoneOffset = null, - ) - RESPIRATORY_RATE -> - RespiratoryRateRecord( - time = - Instant.ofEpochMilli( - startTime - ), - rate = value, - zoneOffset = null, - ) - // AGGREGATE_STEP_COUNT -> StepsRecord() - TOTAL_CALORIES_BURNED -> - TotalCaloriesBurnedRecord( - startTime = - Instant.ofEpochMilli( - startTime - ), - endTime = - Instant.ofEpochMilli( - endTime - ), - energy = - Energy.kilocalories( - value - ), - startZoneOffset = null, - endZoneOffset = null, - ) - MENSTRUATION_FLOW -> MenstruationFlowRecord( - time = Instant.ofEpochMilli(startTime), - flow = value.toInt(), - zoneOffset = null, - ) - BLOOD_PRESSURE_SYSTOLIC -> - throw IllegalArgumentException( - "You must use the [writeBloodPressure] API " - ) - BLOOD_PRESSURE_DIASTOLIC -> - throw IllegalArgumentException( - "You must use the [writeBloodPressure] API " - ) - WORKOUT -> - throw IllegalArgumentException( - "You must use the [writeWorkoutData] API " - ) - NUTRITION -> - throw IllegalArgumentException( - "You must use the [writeMeal] API " - ) - else -> - throw IllegalArgumentException( - "The type $type was not supported by the Health plugin or you must use another API " - ) - } - scope.launch { - try { - healthConnectClient.insertRecords(listOf(record)) - result.success(true) - } catch (e: Exception) { - result.success(false) - } - } - } + is RestingHeartRateRecord -> + return listOf( + mapOf( + "value" to + record.beatsPerMinute, + "date_from" to + record.time + .toEpochMilli(), + "date_to" to + record.time + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ) + ) - fun writeWorkoutHCData(call: MethodCall, result: Result) { - val type = call.argument("activityType")!! - val startTime = Instant.ofEpochMilli(call.argument("startTime")!!) - val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) - val totalEnergyBurned = call.argument("totalEnergyBurned") - val totalDistance = call.argument("totalDistance") - if (workoutTypeMapHealthConnect.containsKey(type) == false) { - result.success(false) - Log.w( - "FLUTTER_HEALTH::ERROR", - "[Health Connect] Workout type not supported" - ) - return - } - val workoutType = workoutTypeMapHealthConnect[type]!! - val title = call.argument("title") ?: type + is FloorsClimbedRecord -> + return listOf( + mapOf( + "value" to record.floors, + "date_from" to + record.startTime + .toEpochMilli(), + "date_to" to + record.endTime + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ) + ) - scope.launch { - try { - val list = mutableListOf() - list.add( - ExerciseSessionRecord( - startTime = startTime, - startZoneOffset = null, - endTime = endTime, - endZoneOffset = null, - exerciseType = workoutType, - title = title, - ), - ) - if (totalDistance != null) { - list.add( - DistanceRecord( - startTime = startTime, - startZoneOffset = null, - endTime = endTime, - endZoneOffset = null, - distance = - Length.meters( - totalDistance.toDouble() - ), - ), - ) - } - if (totalEnergyBurned != null) { - list.add( - TotalCaloriesBurnedRecord( - startTime = startTime, - startZoneOffset = null, - endTime = endTime, - endZoneOffset = null, - energy = - Energy.kilocalories( - totalEnergyBurned - .toDouble() - ), - ), - ) - } - healthConnectClient.insertRecords( - list, + is RespiratoryRateRecord -> + return listOf( + mapOf( + "value" to record.rate, + "date_from" to + record.time + .toEpochMilli(), + "date_to" to + record.time + .toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ) + ) + + is NutritionRecord -> + return listOf( + mapOf( + "calories" to record.energy?.inKilocalories, + "protein" to record.protein?.inGrams, + "carbs" to record.totalCarbohydrate?.inGrams, + "fat" to record.totalFat?.inGrams, + "caffeine" to record.caffeine?.inGrams, + "vitamin_a" to record.vitaminA?.inGrams, + "b1_thiamine" to record.thiamin?.inGrams, + "b2_riboflavin" to record.riboflavin?.inGrams, + "b3_niacin" to record.niacin?.inGrams, + "b5_pantothenic_acid" to record.pantothenicAcid?.inGrams, + "b6_pyridoxine" to record.vitaminB6?.inGrams, + "b7_biotin" to record.biotin?.inGrams, + "b9_folate" to record.folate?.inGrams, + "b12_cobalamin" to record.vitaminB12?.inGrams, + "vitamin_c" to record.vitaminC?.inGrams, + "vitamin_d" to record.vitaminD?.inGrams, + "vitamin_e" to record.vitaminE?.inGrams, + "vitamin_k" to record.vitaminK?.inGrams, + "calcium" to record.calcium?.inGrams, + "chloride" to record.chloride?.inGrams, + "cholesterol" to record.cholesterol?.inGrams, + "choline" to null, + "chromium" to record.chromium?.inGrams, + "copper" to record.copper?.inGrams, + "fat_unsaturated" to record.unsaturatedFat?.inGrams, + "fat_monounsaturated" to record.monounsaturatedFat?.inGrams, + "fat_polyunsaturated" to record.polyunsaturatedFat?.inGrams, + "fat_saturated" to record.saturatedFat?.inGrams, + "fat_trans_monoenoic" to record.transFat?.inGrams, + "fiber" to record.dietaryFiber?.inGrams, + "iodine" to record.iodine?.inGrams, + "iron" to record.iron?.inGrams, + "magnesium" to record.magnesium?.inGrams, + "manganese" to record.manganese?.inGrams, + "molybdenum" to record.molybdenum?.inGrams, + "phosphorus" to record.phosphorus?.inGrams, + "potassium" to record.potassium?.inGrams, + "selenium" to record.selenium?.inGrams, + "sodium" to record.sodium?.inGrams, + "sugar" to record.sugar?.inGrams, + "water" to null, + "zinc" to record.zinc?.inGrams, + "name" to record.name!!, + "meal_type" to + (mapTypeToMealType[ + 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, + ) + ) + + is MenstruationFlowRecord -> + return listOf( + mapOf( + "value" to record.flow, + "date_from" to record.time.toEpochMilli(), + "date_to" to record.time.toEpochMilli(), + "source_id" to "", + "source_name" to + metadata.dataOrigin + .packageName, + ) + ) + // is ExerciseSessionRecord -> return listOf(mapOf("value" to , + // "date_from" to , + // "date_to" to , + // "source_id" to "", + // "source_name" to + // metadata.dataOrigin.packageName)) + else -> + throw IllegalArgumentException( + "Health data type not supported" + ) // TODO: Exception or error? + } + } + + // TODO rewrite sleep to fit new update better --> compare with Apple and see if we should + // not adopt a single type with attached stages approach + private fun writeData(call: MethodCall, result: Result) { + val type = call.argument("dataTypeKey")!! + val startTime = call.argument("startTime")!! + val endTime = call.argument("endTime")!! + val value = call.argument("value")!! + val record = + when (type) { + BODY_FAT_PERCENTAGE -> + BodyFatRecord( + time = + Instant.ofEpochMilli( + startTime + ), + percentage = + Percentage( + value + ), + zoneOffset = null, + ) + + HEIGHT -> + HeightRecord( + time = + Instant.ofEpochMilli( + startTime + ), + height = + Length.meters( + value + ), + zoneOffset = null, + ) + + WEIGHT -> + WeightRecord( + time = + Instant.ofEpochMilli( + startTime + ), + weight = + Mass.kilograms( + value + ), + zoneOffset = null, + ) + + STEPS -> + StepsRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + count = value.toLong(), + startZoneOffset = null, + endZoneOffset = null, + ) + + ACTIVE_ENERGY_BURNED -> + ActiveCaloriesBurnedRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + energy = + Energy.kilocalories( + value + ), + startZoneOffset = null, + endZoneOffset = null, + ) + + HEART_RATE -> + HeartRateRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + samples = + listOf( + HeartRateRecord.Sample( + time = + Instant.ofEpochMilli( + startTime + ), + beatsPerMinute = + value.toLong(), + ), + ), + startZoneOffset = null, + endZoneOffset = null, + ) + + BODY_TEMPERATURE -> + BodyTemperatureRecord( + time = + Instant.ofEpochMilli( + startTime + ), + temperature = + Temperature.celsius( + value + ), + zoneOffset = null, + ) + + BODY_WATER_MASS -> + BodyWaterMassRecord( + time = + Instant.ofEpochMilli( + startTime + ), + mass = + Mass.kilograms( + value + ), + zoneOffset = null, + ) + + BLOOD_OXYGEN -> + OxygenSaturationRecord( + time = + Instant.ofEpochMilli( + startTime + ), + percentage = + Percentage( + value + ), + zoneOffset = null, + ) + + BLOOD_GLUCOSE -> + BloodGlucoseRecord( + time = + Instant.ofEpochMilli( + startTime + ), + level = + BloodGlucose.milligramsPerDeciliter( + value + ), + zoneOffset = null, + ) + + HEART_RATE_VARIABILITY_RMSSD -> + HeartRateVariabilityRmssdRecord( + time = + Instant.ofEpochMilli( + startTime + ), + heartRateVariabilityMillis = + value, + + zoneOffset = null, + ) + + DISTANCE_DELTA -> + DistanceRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + distance = + Length.meters( + value + ), + startZoneOffset = null, + endZoneOffset = null, + ) + + WATER -> + HydrationRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + volume = + Volume.liters( + value + ), + startZoneOffset = null, + endZoneOffset = null, + ) + + SLEEP_ASLEEP -> + SleepSessionRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + startZoneOffset = null, + endZoneOffset = null, + stages = + listOf( + SleepSessionRecord + .Stage( + Instant.ofEpochMilli( + startTime + ), + Instant.ofEpochMilli( + endTime + ), + SleepSessionRecord + .STAGE_TYPE_SLEEPING ) - result.success(true) - Log.i( - "FLUTTER_HEALTH::SUCCESS", - "[Health Connect] Workout was successfully added!" + ), + ) + + SLEEP_LIGHT -> + SleepSessionRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + startZoneOffset = null, + endZoneOffset = null, + stages = + listOf( + SleepSessionRecord + .Stage( + Instant.ofEpochMilli( + startTime + ), + Instant.ofEpochMilli( + endTime + ), + SleepSessionRecord + .STAGE_TYPE_LIGHT ) - } catch (e: Exception) { - Log.w( - "FLUTTER_HEALTH::ERROR", - "[Health Connect] There was an error adding the workout", + ), + ) + + SLEEP_DEEP -> + SleepSessionRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + startZoneOffset = null, + endZoneOffset = null, + stages = + listOf( + SleepSessionRecord + .Stage( + Instant.ofEpochMilli( + startTime + ), + Instant.ofEpochMilli( + endTime + ), + SleepSessionRecord + .STAGE_TYPE_DEEP ) - Log.w("FLUTTER_HEALTH::ERROR", e.message ?: "unknown error") - Log.w("FLUTTER_HEALTH::ERROR", e.stackTrace.toString()) - result.success(false) - } - } - } - - fun writeBloodPressureHC(call: MethodCall, result: Result) { - val systolic = call.argument("systolic")!! - val diastolic = call.argument("diastolic")!! - val startTime = Instant.ofEpochMilli(call.argument("startTime")!!) - val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) - - scope.launch { - try { - healthConnectClient.insertRecords( - listOf( - BloodPressureRecord( - time = startTime, - systolic = - Pressure.millimetersOfMercury( - systolic - ), - diastolic = - Pressure.millimetersOfMercury( - diastolic - ), - zoneOffset = null, - ), - ), + ), + ) + + SLEEP_REM -> + SleepSessionRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + startZoneOffset = null, + endZoneOffset = null, + stages = + listOf( + SleepSessionRecord + .Stage( + Instant.ofEpochMilli( + startTime + ), + Instant.ofEpochMilli( + endTime + ), + SleepSessionRecord + .STAGE_TYPE_REM ) - result.success(true) - Log.i( - "FLUTTER_HEALTH::SUCCESS", - "[Health Connect] Blood pressure was successfully added!", + ), + ) + + SLEEP_OUT_OF_BED -> + SleepSessionRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + startZoneOffset = null, + endZoneOffset = null, + stages = + listOf( + SleepSessionRecord + .Stage( + Instant.ofEpochMilli( + startTime + ), + Instant.ofEpochMilli( + endTime + ), + SleepSessionRecord + .STAGE_TYPE_OUT_OF_BED ) - } catch (e: Exception) { - Log.w( - "FLUTTER_HEALTH::ERROR", - "[Health Connect] There was an error adding the blood pressure", + ), + ) + + SLEEP_AWAKE -> + SleepSessionRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + startZoneOffset = null, + endZoneOffset = null, + stages = + listOf( + SleepSessionRecord + .Stage( + Instant.ofEpochMilli( + startTime + ), + Instant.ofEpochMilli( + endTime + ), + SleepSessionRecord + .STAGE_TYPE_AWAKE ) - Log.w("FLUTTER_HEALTH::ERROR", e.message ?: "unknown error") - Log.w("FLUTTER_HEALTH::ERROR", e.stackTrace.toString()) - result.success(false) - } - } - } - - fun deleteHCData(call: MethodCall, result: Result) { - val type = call.argument("dataTypeKey")!! - val startTime = Instant.ofEpochMilli(call.argument("startTime")!!) - val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) - if (!MapToHCType.containsKey(type)) { - Log.w("FLUTTER_HEALTH::ERROR", "Datatype " + type + " not found in HC") - result.success(false) - return - } - val classType = MapToHCType[type]!! + ), + ) + + SLEEP_SESSION -> + SleepSessionRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + startZoneOffset = null, + endZoneOffset = null, + ) + + RESTING_HEART_RATE -> + RestingHeartRateRecord( + time = + Instant.ofEpochMilli( + startTime + ), + beatsPerMinute = + value.toLong(), + zoneOffset = null, + ) + + BASAL_ENERGY_BURNED -> + BasalMetabolicRateRecord( + time = + Instant.ofEpochMilli( + startTime + ), + basalMetabolicRate = + Power.kilocaloriesPerDay( + value + ), + zoneOffset = null, + ) + + FLIGHTS_CLIMBED -> + FloorsClimbedRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + floors = value, + startZoneOffset = null, + endZoneOffset = null, + ) + + RESPIRATORY_RATE -> + RespiratoryRateRecord( + time = + Instant.ofEpochMilli( + startTime + ), + rate = value, + zoneOffset = null, + ) + // AGGREGATE_STEP_COUNT -> StepsRecord() + TOTAL_CALORIES_BURNED -> + TotalCaloriesBurnedRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + energy = + Energy.kilocalories( + value + ), + startZoneOffset = null, + endZoneOffset = null, + ) + + MENSTRUATION_FLOW -> MenstruationFlowRecord( + time = Instant.ofEpochMilli(startTime), + flow = value.toInt(), + zoneOffset = null, + ) - scope.launch { - try { - healthConnectClient.deleteRecords( - recordType = classType, - timeRangeFilter = - TimeRangeFilter.between( - startTime, - endTime - ), - ) - result.success(true) - } catch (e: Exception) { - result.success(false) - } + BLOOD_PRESSURE_SYSTOLIC -> + throw IllegalArgumentException( + "You must use the [writeBloodPressure] API " + ) + + BLOOD_PRESSURE_DIASTOLIC -> + throw IllegalArgumentException( + "You must use the [writeBloodPressure] API " + ) + + WORKOUT -> + throw IllegalArgumentException( + "You must use the [writeWorkoutData] API " + ) + + NUTRITION -> + throw IllegalArgumentException( + "You must use the [writeMeal] API " + ) + + else -> + throw IllegalArgumentException( + "The type $type was not supported by the Health plugin or you must use another API " + ) + } + scope.launch { + try { + healthConnectClient.insertRecords(listOf(record)) + result.success(true) + } catch (e: Exception) { + result.success(false) + } + } + } + + /** Save a Workout session with options for distance and calories expended */ + private fun writeWorkoutData(call: MethodCall, result: Result) { + val type = call.argument("activityType")!! + val startTime = Instant.ofEpochMilli(call.argument("startTime")!!) + val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) + val totalEnergyBurned = call.argument("totalEnergyBurned") + val totalDistance = call.argument("totalDistance") + if (!workoutTypeMap.containsKey(type)) { + result.success(false) + Log.w( + "FLUTTER_HEALTH::ERROR", + "[Health Connect] Workout type not supported" + ) + return + } + val workoutType = workoutTypeMap[type]!! + val title = call.argument("title") ?: type + + scope.launch { + try { + val list = mutableListOf() + list.add( + ExerciseSessionRecord( + startTime = startTime, + startZoneOffset = null, + endTime = endTime, + endZoneOffset = null, + exerciseType = workoutType, + title = title, + ), + ) + if (totalDistance != null) { + list.add( + DistanceRecord( + startTime = startTime, + startZoneOffset = null, + endTime = endTime, + endZoneOffset = null, + distance = + Length.meters( + totalDistance.toDouble() + ), + ), + ) } + if (totalEnergyBurned != null) { + list.add( + TotalCaloriesBurnedRecord( + startTime = startTime, + startZoneOffset = null, + endTime = endTime, + endZoneOffset = null, + energy = + Energy.kilocalories( + totalEnergyBurned + .toDouble() + ), + ), + ) + } + healthConnectClient.insertRecords( + list, + ) + result.success(true) + Log.i( + "FLUTTER_HEALTH::SUCCESS", + "[Health Connect] Workout was successfully added!" + ) + } catch (e: Exception) { + Log.w( + "FLUTTER_HEALTH::ERROR", + "[Health Connect] There was an error adding the workout", + ) + Log.w("FLUTTER_HEALTH::ERROR", e.message ?: "unknown error") + Log.w("FLUTTER_HEALTH::ERROR", e.stackTrace.toString()) + result.success(false) + } } - - val MapSleepStageToType = - hashMapOf( - 1 to SLEEP_AWAKE, - 2 to SLEEP_ASLEEP, - 3 to SLEEP_OUT_OF_BED, - 4 to SLEEP_LIGHT, - 5 to SLEEP_DEEP, - 6 to SLEEP_REM, - ) - - private val MapMealTypeToTypeHC = - hashMapOf( - BREAKFAST to MEAL_TYPE_BREAKFAST, - LUNCH to MEAL_TYPE_LUNCH, - DINNER to MEAL_TYPE_DINNER, - SNACK to MEAL_TYPE_SNACK, - MEAL_UNKNOWN to MEAL_TYPE_UNKNOWN, - ) - - private val MapTypeToMealTypeHC = - hashMapOf( - MEAL_TYPE_BREAKFAST to BREAKFAST, - MEAL_TYPE_LUNCH to LUNCH, - MEAL_TYPE_DINNER to DINNER, - MEAL_TYPE_SNACK to SNACK, - MEAL_TYPE_UNKNOWN to MEAL_UNKNOWN, - ) - - private val MapMealTypeToType = - hashMapOf( - BREAKFAST to Field.MEAL_TYPE_BREAKFAST, - LUNCH to Field.MEAL_TYPE_LUNCH, - DINNER to Field.MEAL_TYPE_DINNER, - SNACK to Field.MEAL_TYPE_SNACK, - MEAL_UNKNOWN to Field.MEAL_TYPE_UNKNOWN, - ) - - val MapToHCType = - hashMapOf( - BODY_FAT_PERCENTAGE to BodyFatRecord::class, - HEIGHT to HeightRecord::class, - WEIGHT to WeightRecord::class, - STEPS to StepsRecord::class, - AGGREGATE_STEP_COUNT to StepsRecord::class, - ACTIVE_ENERGY_BURNED to ActiveCaloriesBurnedRecord::class, - HEART_RATE to HeartRateRecord::class, - BODY_TEMPERATURE to BodyTemperatureRecord::class, - BODY_WATER_MASS to BodyWaterMassRecord::class, - BLOOD_PRESSURE_SYSTOLIC to BloodPressureRecord::class, - BLOOD_PRESSURE_DIASTOLIC to BloodPressureRecord::class, - BLOOD_OXYGEN to OxygenSaturationRecord::class, - BLOOD_GLUCOSE to BloodGlucoseRecord::class, - HEART_RATE_VARIABILITY_RMSSD to HeartRateVariabilityRmssdRecord::class, - DISTANCE_DELTA to DistanceRecord::class, - WATER to HydrationRecord::class, - SLEEP_ASLEEP to SleepSessionRecord::class, - SLEEP_AWAKE to SleepSessionRecord::class, - SLEEP_LIGHT to SleepSessionRecord::class, - SLEEP_DEEP to SleepSessionRecord::class, - SLEEP_REM to SleepSessionRecord::class, - SLEEP_OUT_OF_BED to SleepSessionRecord::class, - SLEEP_SESSION to SleepSessionRecord::class, - WORKOUT to ExerciseSessionRecord::class, - NUTRITION to NutritionRecord::class, - RESTING_HEART_RATE to RestingHeartRateRecord::class, - BASAL_ENERGY_BURNED to BasalMetabolicRateRecord::class, - FLIGHTS_CLIMBED to FloorsClimbedRecord::class, - RESPIRATORY_RATE to RespiratoryRateRecord::class, - TOTAL_CALORIES_BURNED to TotalCaloriesBurnedRecord::class, - MENSTRUATION_FLOW to MenstruationFlowRecord::class, - // MOVE_MINUTES to TODO: Find alternative? - // TODO: Implement remaining types - // "ActiveCaloriesBurned" to - // ActiveCaloriesBurnedRecord::class, - // "BasalBodyTemperature" to - // BasalBodyTemperatureRecord::class, - // "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, - // "CyclingPedalingCadence" to - // CyclingPedalingCadenceRecord::class, - // "Distance" to DistanceRecord::class, - // "ElevationGained" to ElevationGainedRecord::class, - // "ExerciseSession" to ExerciseSessionRecord::class, - // "FloorsClimbed" to FloorsClimbedRecord::class, - // "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, - // "OxygenSaturation" to OxygenSaturationRecord::class, - // "Power" to PowerRecord::class, - // "RespiratoryRate" to RespiratoryRateRecord::class, - // "RestingHeartRate" to RestingHeartRateRecord::class, - // "SexualActivity" to SexualActivityRecord::class, - // "SleepSession" to SleepSessionRecord::class, - // "SleepStage" to SleepStageRecord::class, - // "Speed" to SpeedRecord::class, - // "StepsCadence" to StepsCadenceRecord::class, - // "Steps" to StepsRecord::class, - // "TotalCaloriesBurned" to - // TotalCaloriesBurnedRecord::class, - // "Vo2Max" to Vo2MaxRecord::class, - // "Weight" to WeightRecord::class, - // "WheelchairPushes" to WheelchairPushesRecord::class, - ) - - val MapToHCAggregateMetric = - hashMapOf( - HEIGHT to HeightRecord.HEIGHT_AVG, - WEIGHT to WeightRecord.WEIGHT_AVG, - STEPS to StepsRecord.COUNT_TOTAL, - AGGREGATE_STEP_COUNT to StepsRecord.COUNT_TOTAL, - ACTIVE_ENERGY_BURNED to - ActiveCaloriesBurnedRecord - .ACTIVE_CALORIES_TOTAL, - HEART_RATE to HeartRateRecord.MEASUREMENTS_COUNT, - DISTANCE_DELTA to DistanceRecord.DISTANCE_TOTAL, - WATER to HydrationRecord.VOLUME_TOTAL, - SLEEP_ASLEEP to SleepSessionRecord.SLEEP_DURATION_TOTAL, - SLEEP_AWAKE to SleepSessionRecord.SLEEP_DURATION_TOTAL, - SLEEP_IN_BED to SleepSessionRecord.SLEEP_DURATION_TOTAL, - TOTAL_CALORIES_BURNED to - TotalCaloriesBurnedRecord.ENERGY_TOTAL - ) + } + + /** Save a Blood Pressure measurement with systolic and diastolic values */ + private fun writeBloodPressure(call: MethodCall, result: Result) { + val systolic = call.argument("systolic")!! + val diastolic = call.argument("diastolic")!! + val startTime = Instant.ofEpochMilli(call.argument("startTime")!!) + + scope.launch { + try { + healthConnectClient.insertRecords( + listOf( + BloodPressureRecord( + time = startTime, + systolic = + Pressure.millimetersOfMercury( + systolic + ), + diastolic = + Pressure.millimetersOfMercury( + diastolic + ), + zoneOffset = null, + ), + ), + ) + result.success(true) + Log.i( + "FLUTTER_HEALTH::SUCCESS", + "[Health Connect] Blood pressure was successfully added!", + ) + } catch (e: Exception) { + Log.w( + "FLUTTER_HEALTH::ERROR", + "[Health Connect] There was an error adding the blood pressure", + ) + Log.w("FLUTTER_HEALTH::ERROR", e.message ?: "unknown error") + Log.w("FLUTTER_HEALTH::ERROR", e.stackTrace.toString()) + result.success(false) + } + } + } + + /** Delete records of the given type in the time range */ + private fun deleteData(call: MethodCall, result: Result) { + val type = call.argument("dataTypeKey")!! + val startTime = Instant.ofEpochMilli(call.argument("startTime")!!) + val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) + if (!mapToType.containsKey(type)) { + Log.w("FLUTTER_HEALTH::ERROR", "Datatype $type not found in HC") + result.success(false) + return + } + val classType = mapToType[type]!! + + scope.launch { + try { + healthConnectClient.deleteRecords( + recordType = classType, + timeRangeFilter = + TimeRangeFilter.between( + startTime, + endTime + ), + ) + result.success(true) + } catch (e: Exception) { + result.success(false) + } + } + } + + private val mapSleepStageToType = + hashMapOf( + 1 to SLEEP_AWAKE, + 2 to SLEEP_ASLEEP, + 3 to SLEEP_OUT_OF_BED, + 4 to SLEEP_LIGHT, + 5 to SLEEP_DEEP, + 6 to SLEEP_REM, + ) + + private val mapMealTypeToType = + hashMapOf( + BREAKFAST to MEAL_TYPE_BREAKFAST, + LUNCH to MEAL_TYPE_LUNCH, + DINNER to MEAL_TYPE_DINNER, + SNACK to MEAL_TYPE_SNACK, + MEAL_UNKNOWN to MEAL_TYPE_UNKNOWN, + ) + + private val mapTypeToMealType = + hashMapOf( + MEAL_TYPE_BREAKFAST to BREAKFAST, + MEAL_TYPE_LUNCH to LUNCH, + MEAL_TYPE_DINNER to DINNER, + MEAL_TYPE_SNACK to SNACK, + MEAL_TYPE_UNKNOWN to MEAL_UNKNOWN, + ) + + + private val mapToType = + hashMapOf( + BODY_FAT_PERCENTAGE to BodyFatRecord::class, + HEIGHT to HeightRecord::class, + WEIGHT to WeightRecord::class, + STEPS to StepsRecord::class, + AGGREGATE_STEP_COUNT to StepsRecord::class, + ACTIVE_ENERGY_BURNED to ActiveCaloriesBurnedRecord::class, + HEART_RATE to HeartRateRecord::class, + BODY_TEMPERATURE to BodyTemperatureRecord::class, + BODY_WATER_MASS to BodyWaterMassRecord::class, + BLOOD_PRESSURE_SYSTOLIC to BloodPressureRecord::class, + BLOOD_PRESSURE_DIASTOLIC to BloodPressureRecord::class, + BLOOD_OXYGEN to OxygenSaturationRecord::class, + BLOOD_GLUCOSE to BloodGlucoseRecord::class, + HEART_RATE_VARIABILITY_RMSSD to HeartRateVariabilityRmssdRecord::class, + DISTANCE_DELTA to DistanceRecord::class, + WATER to HydrationRecord::class, + SLEEP_ASLEEP to SleepSessionRecord::class, + SLEEP_AWAKE to SleepSessionRecord::class, + SLEEP_LIGHT to SleepSessionRecord::class, + SLEEP_DEEP to SleepSessionRecord::class, + SLEEP_REM to SleepSessionRecord::class, + SLEEP_OUT_OF_BED to SleepSessionRecord::class, + SLEEP_SESSION to SleepSessionRecord::class, + WORKOUT to ExerciseSessionRecord::class, + NUTRITION to NutritionRecord::class, + RESTING_HEART_RATE to RestingHeartRateRecord::class, + BASAL_ENERGY_BURNED to BasalMetabolicRateRecord::class, + FLIGHTS_CLIMBED to FloorsClimbedRecord::class, + RESPIRATORY_RATE to RespiratoryRateRecord::class, + TOTAL_CALORIES_BURNED to TotalCaloriesBurnedRecord::class, + MENSTRUATION_FLOW to MenstruationFlowRecord::class, + // TODO: Implement remaining types + // "ActiveCaloriesBurned" to + // ActiveCaloriesBurnedRecord::class, + // "BasalBodyTemperature" to + // BasalBodyTemperatureRecord::class, + // "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, + // "CyclingPedalingCadence" to + // CyclingPedalingCadenceRecord::class, + // "Distance" to DistanceRecord::class, + // "ElevationGained" to ElevationGainedRecord::class, + // "ExerciseSession" to ExerciseSessionRecord::class, + // "FloorsClimbed" to FloorsClimbedRecord::class, + // "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, + // "OxygenSaturation" to OxygenSaturationRecord::class, + // "Power" to PowerRecord::class, + // "RespiratoryRate" to RespiratoryRateRecord::class, + // "RestingHeartRate" to RestingHeartRateRecord::class, + // "SexualActivity" to SexualActivityRecord::class, + // "SleepSession" to SleepSessionRecord::class, + // "SleepStage" to SleepStageRecord::class, + // "Speed" to SpeedRecord::class, + // "StepsCadence" to StepsCadenceRecord::class, + // "Steps" to StepsRecord::class, + // "TotalCaloriesBurned" to + // TotalCaloriesBurnedRecord::class, + // "Vo2Max" to Vo2MaxRecord::class, + // "Weight" to WeightRecord::class, + // "WheelchairPushes" to WheelchairPushesRecord::class, + ) + + private val mapToAggregateMetric = + hashMapOf( + HEIGHT to HeightRecord.HEIGHT_AVG, + WEIGHT to WeightRecord.WEIGHT_AVG, + STEPS to StepsRecord.COUNT_TOTAL, + AGGREGATE_STEP_COUNT to StepsRecord.COUNT_TOTAL, + ACTIVE_ENERGY_BURNED to + ActiveCaloriesBurnedRecord + .ACTIVE_CALORIES_TOTAL, + HEART_RATE to HeartRateRecord.MEASUREMENTS_COUNT, + DISTANCE_DELTA to DistanceRecord.DISTANCE_TOTAL, + WATER to HydrationRecord.VOLUME_TOTAL, + SLEEP_ASLEEP to SleepSessionRecord.SLEEP_DURATION_TOTAL, + SLEEP_AWAKE to SleepSessionRecord.SLEEP_DURATION_TOTAL, + SLEEP_IN_BED to SleepSessionRecord.SLEEP_DURATION_TOTAL, + TOTAL_CALORIES_BURNED to + TotalCaloriesBurnedRecord.ENERGY_TOTAL + ) + + private val workoutTypeMap = + mapOf( + // TODO: add skiing + // TODO: add skating + // TODO: add soccer + // TOOD: look into paddling + // TODO: add runnning + // TODO: look into hockey + "AMERICAN_FOOTBALL" to + ExerciseSessionRecord + .EXERCISE_TYPE_FOOTBALL_AMERICAN, + "AUSTRALIAN_FOOTBALL" to + ExerciseSessionRecord + .EXERCISE_TYPE_FOOTBALL_AUSTRALIAN, + "BADMINTON" to + ExerciseSessionRecord + .EXERCISE_TYPE_BADMINTON, + "BASEBALL" to ExerciseSessionRecord.EXERCISE_TYPE_BASEBALL, + "BASKETBALL" to + ExerciseSessionRecord + .EXERCISE_TYPE_BASKETBALL, + "BIKING" to ExerciseSessionRecord.EXERCISE_TYPE_BIKING, + // "BIKING_STATIONARY" to ExerciseSessionRecord.EXERCISE_TYPE_BIKING_STATIONARY, + "BOXING" to ExerciseSessionRecord.EXERCISE_TYPE_BOXING, + "CALISTHENICS" to + ExerciseSessionRecord + .EXERCISE_TYPE_CALISTHENICS, + "CRICKET" to ExerciseSessionRecord.EXERCISE_TYPE_CRICKET, + // "CROSS_COUNTRY_SKIING" to ExerciseSessionRecord.EXERCISE_TYPE_SKIING_CROSS_COUNTRY, + "DANCING" to ExerciseSessionRecord.EXERCISE_TYPE_DANCING, + // "DOWNHILL_SKIING" to ExerciseSessionRecord.EXERCISE_TYPE_SKIING_DOWNHILL, + "ELLIPTICAL" to + ExerciseSessionRecord + .EXERCISE_TYPE_ELLIPTICAL, + "FENCING" to ExerciseSessionRecord.EXERCISE_TYPE_FENCING, + "FRISBEE_DISC" to + ExerciseSessionRecord + .EXERCISE_TYPE_FRISBEE_DISC, + "GOLF" to ExerciseSessionRecord.EXERCISE_TYPE_GOLF, + "GUIDED_BREATHING" to + ExerciseSessionRecord + .EXERCISE_TYPE_GUIDED_BREATHING, + "GYMNASTICS" to + ExerciseSessionRecord + .EXERCISE_TYPE_GYMNASTICS, + "HANDBALL" to ExerciseSessionRecord.EXERCISE_TYPE_HANDBALL, + "HIGH_INTENSITY_INTERVAL_TRAINING" to + ExerciseSessionRecord + .EXERCISE_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING, + "HIKING" to ExerciseSessionRecord.EXERCISE_TYPE_HIKING, + // "HOCKEY" to ExerciseSessionRecord.EXERCISE_TYPE_HOCKEY, + "ICE_SKATING" to + ExerciseSessionRecord + .EXERCISE_TYPE_ICE_SKATING, + "MARTIAL_ARTS" to + ExerciseSessionRecord + .EXERCISE_TYPE_MARTIAL_ARTS, + "PARAGLIDING" to + ExerciseSessionRecord + .EXERCISE_TYPE_PARAGLIDING, + "PILATES" to ExerciseSessionRecord.EXERCISE_TYPE_PILATES, + "RACQUETBALL" to + ExerciseSessionRecord + .EXERCISE_TYPE_RACQUETBALL, + "ROCK_CLIMBING" to + ExerciseSessionRecord + .EXERCISE_TYPE_ROCK_CLIMBING, + "ROWING" to ExerciseSessionRecord.EXERCISE_TYPE_ROWING, + "ROWING_MACHINE" to + ExerciseSessionRecord + .EXERCISE_TYPE_ROWING_MACHINE, + "RUGBY" to ExerciseSessionRecord.EXERCISE_TYPE_RUGBY, + "RUNNING_TREADMILL" to + ExerciseSessionRecord + .EXERCISE_TYPE_RUNNING_TREADMILL, + "RUNNING" to ExerciseSessionRecord.EXERCISE_TYPE_RUNNING, + "SAILING" to ExerciseSessionRecord.EXERCISE_TYPE_SAILING, + "SCUBA_DIVING" to + ExerciseSessionRecord + .EXERCISE_TYPE_SCUBA_DIVING, + "SKATING" to ExerciseSessionRecord.EXERCISE_TYPE_SKATING, + "SKIING" to ExerciseSessionRecord.EXERCISE_TYPE_SKIING, + "SNOWBOARDING" to + ExerciseSessionRecord + .EXERCISE_TYPE_SNOWBOARDING, + "SNOWSHOEING" to + ExerciseSessionRecord + .EXERCISE_TYPE_SNOWSHOEING, + // "SOCCER" to ExerciseSessionRecord.EXERCISE_TYPE_FOOTBALL_SOCCER, + "SOFTBALL" to ExerciseSessionRecord.EXERCISE_TYPE_SOFTBALL, + "SQUASH" to ExerciseSessionRecord.EXERCISE_TYPE_SQUASH, + "STAIR_CLIMBING_MACHINE" to + ExerciseSessionRecord + .EXERCISE_TYPE_STAIR_CLIMBING_MACHINE, + "STAIR_CLIMBING" to + ExerciseSessionRecord + .EXERCISE_TYPE_STAIR_CLIMBING, + "STRENGTH_TRAINING" to + ExerciseSessionRecord + .EXERCISE_TYPE_STRENGTH_TRAINING, + "SURFING" to ExerciseSessionRecord.EXERCISE_TYPE_SURFING, + "SWIMMING_OPEN_WATER" to + ExerciseSessionRecord + .EXERCISE_TYPE_SWIMMING_OPEN_WATER, + "SWIMMING_POOL" to + ExerciseSessionRecord + .EXERCISE_TYPE_SWIMMING_POOL, + "TABLE_TENNIS" to + ExerciseSessionRecord + .EXERCISE_TYPE_TABLE_TENNIS, + "TENNIS" to ExerciseSessionRecord.EXERCISE_TYPE_TENNIS, + "VOLLEYBALL" to + ExerciseSessionRecord + .EXERCISE_TYPE_VOLLEYBALL, + "WALKING" to ExerciseSessionRecord.EXERCISE_TYPE_WALKING, + "WATER_POLO" to + ExerciseSessionRecord + .EXERCISE_TYPE_WATER_POLO, + "WEIGHTLIFTING" to + ExerciseSessionRecord + .EXERCISE_TYPE_WEIGHTLIFTING, + "WHEELCHAIR" to + ExerciseSessionRecord + .EXERCISE_TYPE_WHEELCHAIR, + "YOGA" to ExerciseSessionRecord.EXERCISE_TYPE_YOGA, + "OTHER" to ExerciseSessionRecord.EXERCISE_TYPE_OTHER_WORKOUT, + ) } diff --git a/packages/health/example/android/app/build.gradle b/packages/health/example/android/app/build.gradle index 95636d301..63e07ba82 100644 --- a/packages/health/example/android/app/build.gradle +++ b/packages/health/example/android/app/build.gradle @@ -64,10 +64,6 @@ dependencies { implementation(composeBom) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" testImplementation 'junit:junit:4.12' - implementation("com.google.android.gms:play-services-fitness:21.1.0") - implementation("com.google.android.gms:play-services-auth:20.2.0") androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' - // The new health connect api - // implementation("androidx.health.connect:connect-client:1.0.0-alpha11") } diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index 59eb830c9..3912bda41 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -81,7 +81,7 @@ class _HealthAppState extends State { void initState() { // configure the health plugin before use. - Health().configure(useHealthConnectIfAvailable: true); + Health().configure(); super.initState(); } @@ -185,7 +185,7 @@ class _HealthAppState extends State { // Add data for supported types // NOTE: These are only the ones supported on Androids new API Health Connect. - // Both Android's Google Fit and iOS' HealthKit have more types that we support in the enum list [HealthDataType] + // Both Android's Health Connect and iOS' HealthKit have more types that we support in the enum list [HealthDataType] // Add more - like AUDIOGRAM, HEADACHE_SEVERE etc. to try them. bool success = true; @@ -217,11 +217,6 @@ class _HealthAppState extends State { type: HealthDataType.HEART_RATE, startTime: earlier, endTime: now); - success &= await Health().writeHealthData( - value: 30, - type: HealthDataType.HEART_RATE_VARIABILITY_RMSSD, - startTime: earlier, - endTime: now); success &= await Health().writeHealthData( value: 37, type: HealthDataType.BODY_TEMPERATURE, @@ -265,7 +260,6 @@ class _HealthAppState extends State { saturation: 98, startTime: earlier, endTime: now, - flowRate: 1.0, ); success &= await Health().writeWorkoutData( activityType: HealthWorkoutActivityType.AMERICAN_FOOTBALL, @@ -591,8 +585,6 @@ class _HealthAppState extends State { Widget _authorizationNotGranted = const Column( children: [ const Text('Authorization not given.'), - const Text( - 'For Google Fit please check your OAUTH2 client ID is correct in Google Developer Console.'), const Text( 'For Google Health Connect please check if you have added the right permissions and services to the manifest file.'), const Text('For Apple Health check your permissions in Apple Health.'), diff --git a/packages/health/example/lib/util.dart b/packages/health/example/lib/util.dart index f1bfcb44f..e135a30f6 100644 --- a/packages/health/example/lib/util.dart +++ b/packages/health/example/lib/util.dart @@ -62,7 +62,7 @@ const List dataTypesIOS = [ /// List of data types available on Android. /// /// Note that these are only the ones supported on Android's Health Connect API. -/// Android's Google Fit have more types that we support in the [HealthDataType] +/// Android's Health Connect has more types that we support in the [HealthDataType] /// enumeration. const List dataTypesAndroid = [ HealthDataType.ACTIVE_ENERGY_BURNED, @@ -79,7 +79,6 @@ const List dataTypesAndroid = [ HealthDataType.HEART_RATE, HealthDataType.HEART_RATE_VARIABILITY_RMSSD, HealthDataType.STEPS, - // HealthDataType.MOVE_MINUTES, // TODO: Find alternative for Health Connect HealthDataType.DISTANCE_DELTA, HealthDataType.RESPIRATORY_RATE, HealthDataType.SLEEP_AWAKE, diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 996c8dc32..9b00a3c2b 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -291,13 +291,6 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else if call.method.elementsEqual("delete") { try! delete(call: call, result: result) } - - /// Disconnect - else if (call.method.elementsEqual("disconnect")){ - // Do nothing. - result(true) - } - } func checkIfHealthDataAvailable(call: FlutterMethodCall, result: @escaping FlutterResult) { @@ -1303,8 +1296,6 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { workoutActivityTypeMap["FLEXIBILITY"] = .flexibility workoutActivityTypeMap["WALKING"] = .walking workoutActivityTypeMap["RUNNING"] = .running - workoutActivityTypeMap["RUNNING_JOGGING"] = .running // Supported due to combining with Android naming - workoutActivityTypeMap["RUNNING_SAND"] = .running // Supported due to combining with Android naming workoutActivityTypeMap["RUNNING_TREADMILL"] = .running // Supported due to combining with Android naming workoutActivityTypeMap["WHEELCHAIR_WALK_PACE"] = .wheelchairWalkPace workoutActivityTypeMap["WHEELCHAIR_RUN_PACE"] = .wheelchairRunPace @@ -1345,9 +1336,6 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { workoutActivityTypeMap["SNOW_SPORTS"] = .snowSports workoutActivityTypeMap["SNOWBOARDING"] = .snowboarding workoutActivityTypeMap["SKATING"] = .skatingSports - workoutActivityTypeMap["SKATING_CROSS,"] = .skatingSports // Supported due to combining with Android naming - workoutActivityTypeMap["SKATING_INDOOR,"] = .skatingSports // Supported due to combining with Android naming - workoutActivityTypeMap["SKATING_INLINE,"] = .skatingSports // Supported due to combining with Android naming workoutActivityTypeMap["PADDLE_SPORTS"] = .paddleSports workoutActivityTypeMap["ROWING"] = .rowing workoutActivityTypeMap["SAILING"] = .sailing diff --git a/packages/health/ios/health.podspec b/packages/health/ios/health.podspec index 443b44c7c..3a8d89370 100644 --- a/packages/health/ios/health.podspec +++ b/packages/health/ios/health.podspec @@ -4,9 +4,9 @@ Pod::Spec.new do |s| s.name = 'health' s.version = '1.0.4' - s.summary = 'Wrapper for the iOS HealthKit and Android GoogleFit services.' + s.summary = 'Wrapper for Apple\'s HealthKit on iOS and Google\'s Health Connect on Android.' s.description = <<-DESC -Wrapper for the iOS HealthKit and Android GoogleFit services. +Wrapper for Apple's HealthKit on iOS and Google's Health Connect on Android. DESC s.homepage = 'https://pub.dev/packages/health' s.license = { :file => '../LICENSE' } diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart index fbbfcc13c..104be599d 100644 --- a/packages/health/lib/health.g.dart +++ b/packages/health/lib/health.g.dart @@ -118,7 +118,6 @@ const _$HealthDataTypeEnumMap = { HealthDataType.DISTANCE_SWIMMING: 'DISTANCE_SWIMMING', HealthDataType.DISTANCE_CYCLING: 'DISTANCE_CYCLING', HealthDataType.FLIGHTS_CLIMBED: 'FLIGHTS_CLIMBED', - HealthDataType.MOVE_MINUTES: 'MOVE_MINUTES', HealthDataType.DISTANCE_DELTA: 'DISTANCE_DELTA', HealthDataType.MINDFULNESS: 'MINDFULNESS', HealthDataType.WATER: 'WATER', @@ -207,7 +206,6 @@ const _$HealthDataUnitEnumMap = { const _$HealthPlatformTypeEnumMap = { HealthPlatformType.appleHealth: 'appleHealth', - HealthPlatformType.googleFit: 'googleFit', HealthPlatformType.googleHealthConnect: 'googleHealthConnect', }; @@ -316,19 +314,20 @@ Map _$WorkoutHealthValueToJson(WorkoutHealthValue instance) { } const _$HealthWorkoutActivityTypeEnumMap = { + HealthWorkoutActivityType.AMERICAN_FOOTBALL: 'AMERICAN_FOOTBALL', HealthWorkoutActivityType.ARCHERY: 'ARCHERY', + HealthWorkoutActivityType.AUSTRALIAN_FOOTBALL: 'AUSTRALIAN_FOOTBALL', HealthWorkoutActivityType.BADMINTON: 'BADMINTON', HealthWorkoutActivityType.BASEBALL: 'BASEBALL', HealthWorkoutActivityType.BASKETBALL: 'BASKETBALL', HealthWorkoutActivityType.BIKING: 'BIKING', HealthWorkoutActivityType.BOXING: 'BOXING', HealthWorkoutActivityType.CRICKET: 'CRICKET', + HealthWorkoutActivityType.CROSS_COUNTRY_SKIING: 'CROSS_COUNTRY_SKIING', HealthWorkoutActivityType.CURLING: 'CURLING', + HealthWorkoutActivityType.DOWNHILL_SKIING: 'DOWNHILL_SKIING', HealthWorkoutActivityType.ELLIPTICAL: 'ELLIPTICAL', HealthWorkoutActivityType.FENCING: 'FENCING', - HealthWorkoutActivityType.AMERICAN_FOOTBALL: 'AMERICAN_FOOTBALL', - HealthWorkoutActivityType.AUSTRALIAN_FOOTBALL: 'AUSTRALIAN_FOOTBALL', - HealthWorkoutActivityType.SOCCER: 'SOCCER', HealthWorkoutActivityType.GOLF: 'GOLF', HealthWorkoutActivityType.GYMNASTICS: 'GYMNASTICS', HealthWorkoutActivityType.HANDBALL: 'HANDBALL', @@ -336,7 +335,6 @@ const _$HealthWorkoutActivityTypeEnumMap = { 'HIGH_INTENSITY_INTERVAL_TRAINING', HealthWorkoutActivityType.HIKING: 'HIKING', HealthWorkoutActivityType.HOCKEY: 'HOCKEY', - HealthWorkoutActivityType.SKATING: 'SKATING', HealthWorkoutActivityType.JUMP_ROPE: 'JUMP_ROPE', HealthWorkoutActivityType.KICKBOXING: 'KICKBOXING', HealthWorkoutActivityType.MARTIAL_ARTS: 'MARTIAL_ARTS', @@ -346,9 +344,9 @@ const _$HealthWorkoutActivityTypeEnumMap = { HealthWorkoutActivityType.RUGBY: 'RUGBY', HealthWorkoutActivityType.RUNNING: 'RUNNING', HealthWorkoutActivityType.SAILING: 'SAILING', - HealthWorkoutActivityType.CROSS_COUNTRY_SKIING: 'CROSS_COUNTRY_SKIING', - HealthWorkoutActivityType.DOWNHILL_SKIING: 'DOWNHILL_SKIING', + HealthWorkoutActivityType.SKATING: 'SKATING', HealthWorkoutActivityType.SNOWBOARDING: 'SNOWBOARDING', + HealthWorkoutActivityType.SOCCER: 'SOCCER', HealthWorkoutActivityType.SOFTBALL: 'SOFTBALL', HealthWorkoutActivityType.SQUASH: 'SQUASH', HealthWorkoutActivityType.STAIR_CLIMBING: 'STAIR_CLIMBING', @@ -359,113 +357,65 @@ const _$HealthWorkoutActivityTypeEnumMap = { HealthWorkoutActivityType.WALKING: 'WALKING', HealthWorkoutActivityType.WATER_POLO: 'WATER_POLO', HealthWorkoutActivityType.YOGA: 'YOGA', + HealthWorkoutActivityType.BARRE: 'BARRE', HealthWorkoutActivityType.BOWLING: 'BOWLING', + HealthWorkoutActivityType.CARDIO_DANCE: 'CARDIO_DANCE', + HealthWorkoutActivityType.CLIMBING: 'CLIMBING', + HealthWorkoutActivityType.COOLDOWN: 'COOLDOWN', + HealthWorkoutActivityType.CORE_TRAINING: 'CORE_TRAINING', HealthWorkoutActivityType.CROSS_TRAINING: 'CROSS_TRAINING', - HealthWorkoutActivityType.TRACK_AND_FIELD: 'TRACK_AND_FIELD', HealthWorkoutActivityType.DISC_SPORTS: 'DISC_SPORTS', - HealthWorkoutActivityType.LACROSSE: 'LACROSSE', - HealthWorkoutActivityType.PREPARATION_AND_RECOVERY: - 'PREPARATION_AND_RECOVERY', + HealthWorkoutActivityType.EQUESTRIAN_SPORTS: 'EQUESTRIAN_SPORTS', + HealthWorkoutActivityType.FISHING: 'FISHING', + HealthWorkoutActivityType.FITNESS_GAMING: 'FITNESS_GAMING', HealthWorkoutActivityType.FLEXIBILITY: 'FLEXIBILITY', - HealthWorkoutActivityType.COOLDOWN: 'COOLDOWN', - HealthWorkoutActivityType.WHEELCHAIR_WALK_PACE: 'WHEELCHAIR_WALK_PACE', - HealthWorkoutActivityType.WHEELCHAIR_RUN_PACE: 'WHEELCHAIR_RUN_PACE', - HealthWorkoutActivityType.HAND_CYCLING: 'HAND_CYCLING', - HealthWorkoutActivityType.CORE_TRAINING: 'CORE_TRAINING', HealthWorkoutActivityType.FUNCTIONAL_STRENGTH_TRAINING: 'FUNCTIONAL_STRENGTH_TRAINING', - HealthWorkoutActivityType.TRADITIONAL_STRENGTH_TRAINING: - 'TRADITIONAL_STRENGTH_TRAINING', - HealthWorkoutActivityType.MIXED_CARDIO: 'MIXED_CARDIO', - HealthWorkoutActivityType.STAIRS: 'STAIRS', - HealthWorkoutActivityType.STEP_TRAINING: 'STEP_TRAINING', - HealthWorkoutActivityType.FITNESS_GAMING: 'FITNESS_GAMING', - HealthWorkoutActivityType.BARRE: 'BARRE', - HealthWorkoutActivityType.CARDIO_DANCE: 'CARDIO_DANCE', - HealthWorkoutActivityType.SOCIAL_DANCE: 'SOCIAL_DANCE', + HealthWorkoutActivityType.HAND_CYCLING: 'HAND_CYCLING', + HealthWorkoutActivityType.HUNTING: 'HUNTING', + HealthWorkoutActivityType.LACROSSE: 'LACROSSE', HealthWorkoutActivityType.MIND_AND_BODY: 'MIND_AND_BODY', + HealthWorkoutActivityType.MIXED_CARDIO: 'MIXED_CARDIO', + HealthWorkoutActivityType.PADDLE_SPORTS: 'PADDLE_SPORTS', HealthWorkoutActivityType.PICKLEBALL: 'PICKLEBALL', - HealthWorkoutActivityType.CLIMBING: 'CLIMBING', - HealthWorkoutActivityType.EQUESTRIAN_SPORTS: 'EQUESTRIAN_SPORTS', - HealthWorkoutActivityType.FISHING: 'FISHING', - HealthWorkoutActivityType.HUNTING: 'HUNTING', HealthWorkoutActivityType.PLAY: 'PLAY', + HealthWorkoutActivityType.PREPARATION_AND_RECOVERY: + 'PREPARATION_AND_RECOVERY', HealthWorkoutActivityType.SNOW_SPORTS: 'SNOW_SPORTS', - HealthWorkoutActivityType.PADDLE_SPORTS: 'PADDLE_SPORTS', + HealthWorkoutActivityType.SOCIAL_DANCE: 'SOCIAL_DANCE', + HealthWorkoutActivityType.STAIRS: 'STAIRS', + HealthWorkoutActivityType.STEP_TRAINING: 'STEP_TRAINING', HealthWorkoutActivityType.SURFING_SPORTS: 'SURFING_SPORTS', + HealthWorkoutActivityType.TAI_CHI: 'TAI_CHI', + HealthWorkoutActivityType.TRACK_AND_FIELD: 'TRACK_AND_FIELD', + HealthWorkoutActivityType.TRADITIONAL_STRENGTH_TRAINING: + 'TRADITIONAL_STRENGTH_TRAINING', HealthWorkoutActivityType.WATER_FITNESS: 'WATER_FITNESS', HealthWorkoutActivityType.WATER_SPORTS: 'WATER_SPORTS', - HealthWorkoutActivityType.TAI_CHI: 'TAI_CHI', + HealthWorkoutActivityType.WHEELCHAIR_RUN_PACE: 'WHEELCHAIR_RUN_PACE', + HealthWorkoutActivityType.WHEELCHAIR_WALK_PACE: 'WHEELCHAIR_WALK_PACE', HealthWorkoutActivityType.WRESTLING: 'WRESTLING', - HealthWorkoutActivityType.AEROBICS: 'AEROBICS', - HealthWorkoutActivityType.BIATHLON: 'BIATHLON', - HealthWorkoutActivityType.BIKING_HAND: 'BIKING_HAND', - HealthWorkoutActivityType.BIKING_MOUNTAIN: 'BIKING_MOUNTAIN', - HealthWorkoutActivityType.BIKING_ROAD: 'BIKING_ROAD', - HealthWorkoutActivityType.BIKING_SPINNING: 'BIKING_SPINNING', HealthWorkoutActivityType.BIKING_STATIONARY: 'BIKING_STATIONARY', - HealthWorkoutActivityType.BIKING_UTILITY: 'BIKING_UTILITY', HealthWorkoutActivityType.CALISTHENICS: 'CALISTHENICS', - HealthWorkoutActivityType.CIRCUIT_TRAINING: 'CIRCUIT_TRAINING', - HealthWorkoutActivityType.CROSS_FIT: 'CROSS_FIT', HealthWorkoutActivityType.DANCING: 'DANCING', - HealthWorkoutActivityType.DIVING: 'DIVING', - HealthWorkoutActivityType.ELEVATOR: 'ELEVATOR', - HealthWorkoutActivityType.ERGOMETER: 'ERGOMETER', - HealthWorkoutActivityType.ESCALATOR: 'ESCALATOR', HealthWorkoutActivityType.FRISBEE_DISC: 'FRISBEE_DISC', - HealthWorkoutActivityType.GARDENING: 'GARDENING', HealthWorkoutActivityType.GUIDED_BREATHING: 'GUIDED_BREATHING', - HealthWorkoutActivityType.HORSEBACK_RIDING: 'HORSEBACK_RIDING', - HealthWorkoutActivityType.HOUSEWORK: 'HOUSEWORK', - HealthWorkoutActivityType.INTERVAL_TRAINING: 'INTERVAL_TRAINING', - HealthWorkoutActivityType.IN_VEHICLE: 'IN_VEHICLE', HealthWorkoutActivityType.ICE_SKATING: 'ICE_SKATING', - HealthWorkoutActivityType.KAYAKING: 'KAYAKING', - HealthWorkoutActivityType.KETTLEBELL_TRAINING: 'KETTLEBELL_TRAINING', - HealthWorkoutActivityType.KICK_SCOOTER: 'KICK_SCOOTER', - HealthWorkoutActivityType.KITE_SURFING: 'KITE_SURFING', - HealthWorkoutActivityType.MEDITATION: 'MEDITATION', - HealthWorkoutActivityType.MIXED_MARTIAL_ARTS: 'MIXED_MARTIAL_ARTS', - HealthWorkoutActivityType.P90X: 'P90X', HealthWorkoutActivityType.PARAGLIDING: 'PARAGLIDING', - HealthWorkoutActivityType.POLO: 'POLO', HealthWorkoutActivityType.ROCK_CLIMBING: 'ROCK_CLIMBING', HealthWorkoutActivityType.ROWING_MACHINE: 'ROWING_MACHINE', - HealthWorkoutActivityType.RUNNING_JOGGING: 'RUNNING_JOGGING', - HealthWorkoutActivityType.RUNNING_SAND: 'RUNNING_SAND', HealthWorkoutActivityType.RUNNING_TREADMILL: 'RUNNING_TREADMILL', HealthWorkoutActivityType.SCUBA_DIVING: 'SCUBA_DIVING', - HealthWorkoutActivityType.SKATING_CROSS: 'SKATING_CROSS', - HealthWorkoutActivityType.SKATING_INDOOR: 'SKATING_INDOOR', - HealthWorkoutActivityType.SKATING_INLINE: 'SKATING_INLINE', HealthWorkoutActivityType.SKIING: 'SKIING', - HealthWorkoutActivityType.SKIING_BACK_COUNTRY: 'SKIING_BACK_COUNTRY', - HealthWorkoutActivityType.SKIING_KITE: 'SKIING_KITE', - HealthWorkoutActivityType.SKIING_ROLLER: 'SKIING_ROLLER', - HealthWorkoutActivityType.SLEDDING: 'SLEDDING', - HealthWorkoutActivityType.SNOWMOBILE: 'SNOWMOBILE', HealthWorkoutActivityType.SNOWSHOEING: 'SNOWSHOEING', HealthWorkoutActivityType.STAIR_CLIMBING_MACHINE: 'STAIR_CLIMBING_MACHINE', - HealthWorkoutActivityType.STANDUP_PADDLEBOARDING: 'STANDUP_PADDLEBOARDING', - HealthWorkoutActivityType.STILL: 'STILL', HealthWorkoutActivityType.STRENGTH_TRAINING: 'STRENGTH_TRAINING', HealthWorkoutActivityType.SURFING: 'SURFING', HealthWorkoutActivityType.SWIMMING_OPEN_WATER: 'SWIMMING_OPEN_WATER', HealthWorkoutActivityType.SWIMMING_POOL: 'SWIMMING_POOL', - HealthWorkoutActivityType.TEAM_SPORTS: 'TEAM_SPORTS', - HealthWorkoutActivityType.TILTING: 'TILTING', - HealthWorkoutActivityType.VOLLEYBALL_BEACH: 'VOLLEYBALL_BEACH', - HealthWorkoutActivityType.VOLLEYBALL_INDOOR: 'VOLLEYBALL_INDOOR', - HealthWorkoutActivityType.WAKEBOARDING: 'WAKEBOARDING', - HealthWorkoutActivityType.WALKING_FITNESS: 'WALKING_FITNESS', - HealthWorkoutActivityType.WALKING_NORDIC: 'WALKING_NORDIC', - HealthWorkoutActivityType.WALKING_STROLLER: 'WALKING_STROLLER', HealthWorkoutActivityType.WALKING_TREADMILL: 'WALKING_TREADMILL', HealthWorkoutActivityType.WEIGHTLIFTING: 'WEIGHTLIFTING', HealthWorkoutActivityType.WHEELCHAIR: 'WHEELCHAIR', - HealthWorkoutActivityType.WINDSURFING: 'WINDSURFING', - HealthWorkoutActivityType.ZUMBA: 'ZUMBA', HealthWorkoutActivityType.OTHER: 'OTHER', }; diff --git a/packages/health/lib/health.json.dart b/packages/health/lib/health.json.dart index 72a43924b..351098a28 100644 --- a/packages/health/lib/health.json.dart +++ b/packages/health/lib/health.json.dart @@ -15,7 +15,6 @@ void _registerFromJsonFunctions() { leftEarSensitivities: [], rightEarSensitivities: [], ), - WorkoutHealthValue(workoutActivityType: HealthWorkoutActivityType.AEROBICS), ElectrocardiogramHealthValue(voltageValues: []), ElectrocardiogramVoltageValue(voltage: 12, timeSinceSampleStart: 0), NutritionHealthValue(), diff --git a/packages/health/lib/src/health_data_point.dart b/packages/health/lib/src/health_data_point.dart index 417991fb2..b98734c5a 100644 --- a/packages/health/lib/src/health_data_point.dart +++ b/packages/health/lib/src/health_data_point.dart @@ -1,10 +1,10 @@ part of '../health.dart'; /// Types of health platforms. -enum HealthPlatformType { appleHealth, googleFit, googleHealthConnect } +enum HealthPlatformType { appleHealth, googleHealthConnect } /// A [HealthDataPoint] object corresponds to a data point capture from -/// Apple HealthKit or Google Fit or Google Health Connect with a [HealthValue] +/// Apple HealthKit or Google Health Connect with a [HealthValue] /// as value. @JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) class HealthDataPoint { diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 6fe05ef05..a55bc49e4 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -27,7 +27,6 @@ class Health { String? _deviceId; final _deviceInfo = DeviceInfoPlugin(); - bool _useHealthConnectIfAvailable = false; Health._() { _registerFromJsonFunctions(); @@ -39,9 +38,7 @@ class Health { /// The type of platform of this device. HealthPlatformType get platformType => Platform.isIOS ? HealthPlatformType.appleHealth - : useHealthConnectIfAvailable - ? HealthPlatformType.googleHealthConnect - : HealthPlatformType.googleFit; + : HealthPlatformType.googleHealthConnect; /// The id of this device. /// @@ -50,24 +47,12 @@ class Health { String get deviceId => _deviceId ?? 'unknown'; /// Configure the health plugin. Must be called before using the plugin. - /// - /// If [useHealthConnectIfAvailable] is true, Google Health Connect on - /// Android will be used. Has no effect on iOS. - Future configure({bool useHealthConnectIfAvailable = false}) async { - if (Platform.isAndroid) { - _deviceId = (await _deviceInfo.androidInfo).id; - _useHealthConnectIfAvailable = useHealthConnectIfAvailable; - await _channel.invokeMethod('useHealthConnectIfAvailable'); - } else { - _deviceId = (await _deviceInfo.iosInfo).identifierForVendor; - } + Future configure() async { + _deviceId = Platform.isAndroid + ? (await _deviceInfo.androidInfo).id + : (await _deviceInfo.iosInfo).identifierForVendor; } - /// Is this plugin using Health Connect (true) or Google Fit (false)? - /// - /// This is set in the [configure] method. - bool get useHealthConnectIfAvailable => _useHealthConnectIfAvailable; - /// Check if a given data type is available on the platform bool isDataTypeAvailable(HealthDataType dataType) => Platform.isAndroid ? dataTypeKeysAndroid.contains(dataType) @@ -121,10 +106,9 @@ class Health { }); } - /// Revokes permissions of all types. - /// - /// Uses `disableFit()` on Google Fit. + /// Revokes Google Health Connect permissions on Android of all types. /// + /// NOTE: The app must be completely killed and restarted for the changes to take effect. /// Not implemented on iOS as there is no way to programmatically remove access. Future revokePermissions() async { try { @@ -175,33 +159,6 @@ class Health { } } - /// Disconnect from Google fit. - /// - /// Not supported on iOS and Google Health Connect, and the method does nothing. - Future disconnect( - List types, { - List? permissions, - }) async { - if (permissions != null && permissions.length != types.length) { - throw ArgumentError( - 'The length of [types] must be same as that of [permissions].'); - } - - final mTypes = List.from(types, growable: true); - final mPermissions = permissions == null - ? List.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 keys = mTypes.map((dataType) => dataType.name).toList(); - - return await _channel.invokeMethod( - 'disconnect', {'types': keys, "permissions": mPermissions}); - } - /// Requests permissions to access health data [types]. /// /// Returns true if successful, false otherwise. @@ -353,6 +310,10 @@ class Health { throw ArgumentError( "Adding workouts should be done using the writeWorkoutData method."); } + // If not implemented on platform, throw an exception + if (!isDataTypeAvailable(type)) { + throw HealthException(type, 'Not available on platform $platformType'); + } endTime ??= startTime; if (startTime.isAfter(endTime)) { throw ArgumentError("startTime must be equal or earlier than endTime"); @@ -468,8 +429,6 @@ class Health { /// /// Parameters: /// * [saturation] - the saturation of the blood oxygen in percentage - /// * [flowRate] - optional supplemental oxygen flow rate, only supported on - /// Google Fit (default 0.0) /// * [startTime] - the start time when this [saturation] is measured. /// Must be equal to or earlier than [endTime]. /// * [endTime] - the end time when this [saturation] is measured. @@ -478,7 +437,6 @@ class Health { /// is measured only at a specific point in time (default). Future writeBloodOxygen({ required double saturation, - double flowRate = 0.0, required DateTime startTime, DateTime? endTime, }) async { @@ -497,7 +455,6 @@ class Health { } else if (Platform.isAndroid) { Map args = { 'value': saturation, - 'flowRate': flowRate, 'startTime': startTime.millisecondsSinceEpoch, 'endTime': endTime.millisecondsSinceEpoch, 'dataTypeKey': HealthDataType.BLOOD_OXYGEN.name, @@ -507,7 +464,7 @@ class Health { return success ?? false; } - /// Saves meal record into Apple Health or Google Fit / Health Connect. + /// Saves meal record into Apple Health or Health Connect. /// /// Returns true if successful, false otherwise. /// @@ -1020,8 +977,6 @@ class Health { /// Get the total number of steps within a specific time period. /// Returns null if not successful. - /// - /// Is a fix according to https://stackoverflow.com/questions/29414386/step-count-retrieved-through-google-fit-api-does-not-match-step-count-displayed/29415091#29415091 Future getTotalStepsInInterval(DateTime startTime, DateTime endTime, {bool includeManualEntry = true}) async { final args = { @@ -1055,7 +1010,7 @@ class Health { "HealthDataType was not aligned correctly - please report bug at https://github.com/cph-cachet/flutter-plugins/issues"), }; - /// Write workout data to Apple Health or Google Fit or Google Health Connect. + /// Write workout data to Apple Health or Google Health Connect. /// /// Returns true if the workout data was successfully added. /// @@ -1106,84 +1061,84 @@ class Health { bool _isOnIOS(HealthWorkoutActivityType type) { // Returns true if the type is part of the iOS set return { + HealthWorkoutActivityType.AMERICAN_FOOTBALL, HealthWorkoutActivityType.ARCHERY, + HealthWorkoutActivityType.AUSTRALIAN_FOOTBALL, HealthWorkoutActivityType.BADMINTON, + HealthWorkoutActivityType.BARRE, HealthWorkoutActivityType.BASEBALL, HealthWorkoutActivityType.BASKETBALL, HealthWorkoutActivityType.BIKING, + HealthWorkoutActivityType.BOWLING, HealthWorkoutActivityType.BOXING, + HealthWorkoutActivityType.CARDIO_DANCE, + HealthWorkoutActivityType.CLIMBING, + HealthWorkoutActivityType.COOLDOWN, + HealthWorkoutActivityType.CORE_TRAINING, HealthWorkoutActivityType.CRICKET, + HealthWorkoutActivityType.CROSS_COUNTRY_SKIING, + HealthWorkoutActivityType.CROSS_TRAINING, HealthWorkoutActivityType.CURLING, + HealthWorkoutActivityType.DISC_SPORTS, + HealthWorkoutActivityType.DOWNHILL_SKIING, HealthWorkoutActivityType.ELLIPTICAL, + HealthWorkoutActivityType.EQUESTRIAN_SPORTS, HealthWorkoutActivityType.FENCING, - HealthWorkoutActivityType.AMERICAN_FOOTBALL, - HealthWorkoutActivityType.AUSTRALIAN_FOOTBALL, - HealthWorkoutActivityType.SOCCER, + HealthWorkoutActivityType.FISHING, + HealthWorkoutActivityType.FITNESS_GAMING, + HealthWorkoutActivityType.FLEXIBILITY, + HealthWorkoutActivityType.FUNCTIONAL_STRENGTH_TRAINING, HealthWorkoutActivityType.GOLF, HealthWorkoutActivityType.GYMNASTICS, + HealthWorkoutActivityType.HAND_CYCLING, HealthWorkoutActivityType.HANDBALL, HealthWorkoutActivityType.HIGH_INTENSITY_INTERVAL_TRAINING, HealthWorkoutActivityType.HIKING, HealthWorkoutActivityType.HOCKEY, - HealthWorkoutActivityType.SKATING, + HealthWorkoutActivityType.HUNTING, HealthWorkoutActivityType.JUMP_ROPE, HealthWorkoutActivityType.KICKBOXING, + HealthWorkoutActivityType.LACROSSE, HealthWorkoutActivityType.MARTIAL_ARTS, + HealthWorkoutActivityType.MIND_AND_BODY, + HealthWorkoutActivityType.MIXED_CARDIO, + HealthWorkoutActivityType.OTHER, + HealthWorkoutActivityType.PADDLE_SPORTS, + HealthWorkoutActivityType.PICKLEBALL, HealthWorkoutActivityType.PILATES, + HealthWorkoutActivityType.PLAY, + HealthWorkoutActivityType.PREPARATION_AND_RECOVERY, HealthWorkoutActivityType.RACQUETBALL, HealthWorkoutActivityType.ROWING, HealthWorkoutActivityType.RUGBY, HealthWorkoutActivityType.RUNNING, HealthWorkoutActivityType.SAILING, - HealthWorkoutActivityType.CROSS_COUNTRY_SKIING, - HealthWorkoutActivityType.DOWNHILL_SKIING, + HealthWorkoutActivityType.SKATING, + HealthWorkoutActivityType.SNOW_SPORTS, HealthWorkoutActivityType.SNOWBOARDING, + HealthWorkoutActivityType.SOCCER, + HealthWorkoutActivityType.SOCIAL_DANCE, HealthWorkoutActivityType.SOFTBALL, HealthWorkoutActivityType.SQUASH, HealthWorkoutActivityType.STAIR_CLIMBING, + HealthWorkoutActivityType.STAIRS, + HealthWorkoutActivityType.STEP_TRAINING, + HealthWorkoutActivityType.SURFING_SPORTS, HealthWorkoutActivityType.SWIMMING, HealthWorkoutActivityType.TABLE_TENNIS, + HealthWorkoutActivityType.TAI_CHI, HealthWorkoutActivityType.TENNIS, - HealthWorkoutActivityType.VOLLEYBALL, - HealthWorkoutActivityType.WALKING, - HealthWorkoutActivityType.WATER_POLO, - HealthWorkoutActivityType.YOGA, - HealthWorkoutActivityType.BOWLING, - HealthWorkoutActivityType.CROSS_TRAINING, HealthWorkoutActivityType.TRACK_AND_FIELD, - HealthWorkoutActivityType.DISC_SPORTS, - HealthWorkoutActivityType.LACROSSE, - HealthWorkoutActivityType.PREPARATION_AND_RECOVERY, - HealthWorkoutActivityType.FLEXIBILITY, - HealthWorkoutActivityType.COOLDOWN, - HealthWorkoutActivityType.WHEELCHAIR_WALK_PACE, - HealthWorkoutActivityType.WHEELCHAIR_RUN_PACE, - HealthWorkoutActivityType.HAND_CYCLING, - HealthWorkoutActivityType.CORE_TRAINING, - HealthWorkoutActivityType.FUNCTIONAL_STRENGTH_TRAINING, HealthWorkoutActivityType.TRADITIONAL_STRENGTH_TRAINING, - HealthWorkoutActivityType.MIXED_CARDIO, - HealthWorkoutActivityType.STAIRS, - HealthWorkoutActivityType.STEP_TRAINING, - HealthWorkoutActivityType.FITNESS_GAMING, - HealthWorkoutActivityType.BARRE, - HealthWorkoutActivityType.CARDIO_DANCE, - HealthWorkoutActivityType.SOCIAL_DANCE, - HealthWorkoutActivityType.MIND_AND_BODY, - HealthWorkoutActivityType.PICKLEBALL, - HealthWorkoutActivityType.CLIMBING, - HealthWorkoutActivityType.EQUESTRIAN_SPORTS, - HealthWorkoutActivityType.FISHING, - HealthWorkoutActivityType.HUNTING, - HealthWorkoutActivityType.PLAY, - HealthWorkoutActivityType.SNOW_SPORTS, - HealthWorkoutActivityType.PADDLE_SPORTS, - HealthWorkoutActivityType.SURFING_SPORTS, + HealthWorkoutActivityType.VOLLEYBALL, + HealthWorkoutActivityType.WALKING, HealthWorkoutActivityType.WATER_FITNESS, + HealthWorkoutActivityType.WATER_POLO, HealthWorkoutActivityType.WATER_SPORTS, - HealthWorkoutActivityType.TAI_CHI, + HealthWorkoutActivityType.WHEELCHAIR_RUN_PACE, + HealthWorkoutActivityType.WHEELCHAIR_WALK_PACE, HealthWorkoutActivityType.WRESTLING, - HealthWorkoutActivityType.OTHER, + HealthWorkoutActivityType.YOGA, }.contains(type); } @@ -1192,28 +1147,26 @@ class Health { // Returns true if the type is part of the Android set return { // Both + HealthWorkoutActivityType.AMERICAN_FOOTBALL, HealthWorkoutActivityType.ARCHERY, + HealthWorkoutActivityType.AUSTRALIAN_FOOTBALL, HealthWorkoutActivityType.BADMINTON, HealthWorkoutActivityType.BASEBALL, HealthWorkoutActivityType.BASKETBALL, HealthWorkoutActivityType.BIKING, HealthWorkoutActivityType.BOXING, HealthWorkoutActivityType.CRICKET, + HealthWorkoutActivityType.CROSS_COUNTRY_SKIING, HealthWorkoutActivityType.CURLING, + HealthWorkoutActivityType.DOWNHILL_SKIING, HealthWorkoutActivityType.ELLIPTICAL, HealthWorkoutActivityType.FENCING, - HealthWorkoutActivityType.AMERICAN_FOOTBALL, - HealthWorkoutActivityType.AUSTRALIAN_FOOTBALL, - HealthWorkoutActivityType.SOCCER, HealthWorkoutActivityType.GOLF, HealthWorkoutActivityType.GYMNASTICS, HealthWorkoutActivityType.HANDBALL, HealthWorkoutActivityType.HIGH_INTENSITY_INTERVAL_TRAINING, HealthWorkoutActivityType.HIKING, HealthWorkoutActivityType.HOCKEY, - HealthWorkoutActivityType.SKATING, - HealthWorkoutActivityType.JUMP_ROPE, - HealthWorkoutActivityType.KICKBOXING, HealthWorkoutActivityType.MARTIAL_ARTS, HealthWorkoutActivityType.PILATES, HealthWorkoutActivityType.RACQUETBALL, @@ -1221,13 +1174,12 @@ class Health { HealthWorkoutActivityType.RUGBY, HealthWorkoutActivityType.RUNNING, HealthWorkoutActivityType.SAILING, - HealthWorkoutActivityType.CROSS_COUNTRY_SKIING, - HealthWorkoutActivityType.DOWNHILL_SKIING, + HealthWorkoutActivityType.SKATING, HealthWorkoutActivityType.SNOWBOARDING, + HealthWorkoutActivityType.SOCCER, HealthWorkoutActivityType.SOFTBALL, HealthWorkoutActivityType.SQUASH, HealthWorkoutActivityType.STAIR_CLIMBING, - HealthWorkoutActivityType.SWIMMING, HealthWorkoutActivityType.TABLE_TENNIS, HealthWorkoutActivityType.TENNIS, HealthWorkoutActivityType.VOLLEYBALL, @@ -1236,76 +1188,27 @@ class Health { HealthWorkoutActivityType.YOGA, // Android only - // Once Google Fit is removed, this list needs to be changed - HealthWorkoutActivityType.AEROBICS, - HealthWorkoutActivityType.BIATHLON, - HealthWorkoutActivityType.BIKING_HAND, - HealthWorkoutActivityType.BIKING_MOUNTAIN, - HealthWorkoutActivityType.BIKING_ROAD, - HealthWorkoutActivityType.BIKING_SPINNING, HealthWorkoutActivityType.BIKING_STATIONARY, - HealthWorkoutActivityType.BIKING_UTILITY, HealthWorkoutActivityType.CALISTHENICS, - HealthWorkoutActivityType.CIRCUIT_TRAINING, - HealthWorkoutActivityType.CROSS_FIT, HealthWorkoutActivityType.DANCING, - HealthWorkoutActivityType.DIVING, - HealthWorkoutActivityType.ELEVATOR, - HealthWorkoutActivityType.ERGOMETER, - HealthWorkoutActivityType.ESCALATOR, HealthWorkoutActivityType.FRISBEE_DISC, - HealthWorkoutActivityType.GARDENING, HealthWorkoutActivityType.GUIDED_BREATHING, - HealthWorkoutActivityType.HORSEBACK_RIDING, - HealthWorkoutActivityType.HOUSEWORK, - HealthWorkoutActivityType.INTERVAL_TRAINING, - HealthWorkoutActivityType.IN_VEHICLE, HealthWorkoutActivityType.ICE_SKATING, - HealthWorkoutActivityType.KAYAKING, - HealthWorkoutActivityType.KETTLEBELL_TRAINING, - HealthWorkoutActivityType.KICK_SCOOTER, - HealthWorkoutActivityType.KITE_SURFING, - HealthWorkoutActivityType.MEDITATION, - HealthWorkoutActivityType.MIXED_MARTIAL_ARTS, - HealthWorkoutActivityType.P90X, HealthWorkoutActivityType.PARAGLIDING, - HealthWorkoutActivityType.POLO, HealthWorkoutActivityType.ROCK_CLIMBING, HealthWorkoutActivityType.ROWING_MACHINE, - HealthWorkoutActivityType.RUNNING_JOGGING, - HealthWorkoutActivityType.RUNNING_SAND, HealthWorkoutActivityType.RUNNING_TREADMILL, HealthWorkoutActivityType.SCUBA_DIVING, - HealthWorkoutActivityType.SKATING_CROSS, - HealthWorkoutActivityType.SKATING_INDOOR, - HealthWorkoutActivityType.SKATING_INLINE, HealthWorkoutActivityType.SKIING, - HealthWorkoutActivityType.SKIING_BACK_COUNTRY, - HealthWorkoutActivityType.SKIING_KITE, - HealthWorkoutActivityType.SKIING_ROLLER, - HealthWorkoutActivityType.SLEDDING, - HealthWorkoutActivityType.SNOWMOBILE, HealthWorkoutActivityType.SNOWSHOEING, HealthWorkoutActivityType.STAIR_CLIMBING_MACHINE, - HealthWorkoutActivityType.STANDUP_PADDLEBOARDING, - HealthWorkoutActivityType.STILL, HealthWorkoutActivityType.STRENGTH_TRAINING, HealthWorkoutActivityType.SURFING, HealthWorkoutActivityType.SWIMMING_OPEN_WATER, HealthWorkoutActivityType.SWIMMING_POOL, - HealthWorkoutActivityType.TEAM_SPORTS, - HealthWorkoutActivityType.TILTING, - HealthWorkoutActivityType.VOLLEYBALL_BEACH, - HealthWorkoutActivityType.VOLLEYBALL_INDOOR, - HealthWorkoutActivityType.WAKEBOARDING, - HealthWorkoutActivityType.WALKING_FITNESS, - HealthWorkoutActivityType.WALKING_NORDIC, - HealthWorkoutActivityType.WALKING_STROLLER, HealthWorkoutActivityType.WALKING_TREADMILL, HealthWorkoutActivityType.WEIGHTLIFTING, HealthWorkoutActivityType.WHEELCHAIR, - HealthWorkoutActivityType.WINDSURFING, - HealthWorkoutActivityType.ZUMBA, HealthWorkoutActivityType.OTHER, }.contains(type); } diff --git a/packages/health/lib/src/health_value_types.dart b/packages/health/lib/src/health_value_types.dart index 6c161ac1e..502328b51 100644 --- a/packages/health/lib/src/health_value_types.dart +++ b/packages/health/lib/src/health_value_types.dart @@ -13,7 +13,7 @@ class HealthValue extends Serializable { Map toJson() => _$HealthValueToJson(this); } -/// A numerical value from Apple HealthKit or Google Fit +/// A numerical value from Apple HealthKit or Google Health Connect /// such as integer or double. E.g. 1, 2.9, -3 /// /// Parameters: diff --git a/packages/health/lib/src/heath_data_types.dart b/packages/health/lib/src/heath_data_types.dart index ba04a21e2..014cfe1da 100644 --- a/packages/health/lib/src/heath_data_types.dart +++ b/packages/health/lib/src/heath_data_types.dart @@ -68,7 +68,6 @@ enum HealthDataType { DISTANCE_SWIMMING, DISTANCE_CYCLING, FLIGHTS_CLIMBED, - MOVE_MINUTES, DISTANCE_DELTA, MINDFULNESS, WATER, @@ -226,7 +225,6 @@ const List dataTypeKeysAndroid = [ HealthDataType.HEIGHT, HealthDataType.STEPS, HealthDataType.WEIGHT, - HealthDataType.MOVE_MINUTES, HealthDataType.DISTANCE_DELTA, HealthDataType.SLEEP_AWAKE, HealthDataType.SLEEP_ASLEEP, @@ -315,7 +313,6 @@ const Map dataTypeToUnit = { HealthDataType.DISTANCE_SWIMMING: HealthDataUnit.METER, HealthDataType.DISTANCE_CYCLING: HealthDataUnit.METER, HealthDataType.FLIGHTS_CLIMBED: HealthDataUnit.COUNT, - HealthDataType.MOVE_MINUTES: HealthDataUnit.MINUTE, HealthDataType.DISTANCE_DELTA: HealthDataUnit.METER, HealthDataType.WATER: HealthDataUnit.LITER, @@ -451,26 +448,26 @@ enum HealthWorkoutActivityType { // Commented for which platform the type are supported // Both + AMERICAN_FOOTBALL, ARCHERY, + AUSTRALIAN_FOOTBALL, BADMINTON, BASEBALL, BASKETBALL, BIKING, // This also entails the iOS version where it is called CYCLING BOXING, CRICKET, + CROSS_COUNTRY_SKIING, CURLING, + DOWNHILL_SKIING, ELLIPTICAL, FENCING, - AMERICAN_FOOTBALL, - AUSTRALIAN_FOOTBALL, - SOCCER, GOLF, GYMNASTICS, HANDBALL, HIGH_INTENSITY_INTERVAL_TRAINING, HIKING, HOCKEY, - SKATING, JUMP_ROPE, KICKBOXING, MARTIAL_ARTS, @@ -480,9 +477,9 @@ enum HealthWorkoutActivityType { RUGBY, RUNNING, SAILING, - CROSS_COUNTRY_SKIING, - DOWNHILL_SKIING, + SKATING, SNOWBOARDING, + SOCCER, SOFTBALL, SQUASH, STAIR_CLIMBING, @@ -495,112 +492,64 @@ enum HealthWorkoutActivityType { YOGA, // iOS only + BARRE, BOWLING, + CARDIO_DANCE, + CLIMBING, + COOLDOWN, + CORE_TRAINING, CROSS_TRAINING, - TRACK_AND_FIELD, DISC_SPORTS, - LACROSSE, - PREPARATION_AND_RECOVERY, + EQUESTRIAN_SPORTS, + FISHING, + FITNESS_GAMING, FLEXIBILITY, - COOLDOWN, - WHEELCHAIR_WALK_PACE, - WHEELCHAIR_RUN_PACE, - HAND_CYCLING, - CORE_TRAINING, FUNCTIONAL_STRENGTH_TRAINING, - TRADITIONAL_STRENGTH_TRAINING, - MIXED_CARDIO, - STAIRS, - STEP_TRAINING, - FITNESS_GAMING, - BARRE, - CARDIO_DANCE, - SOCIAL_DANCE, + HAND_CYCLING, + HUNTING, + LACROSSE, MIND_AND_BODY, + MIXED_CARDIO, + PADDLE_SPORTS, PICKLEBALL, - CLIMBING, - EQUESTRIAN_SPORTS, - FISHING, - HUNTING, PLAY, + PREPARATION_AND_RECOVERY, SNOW_SPORTS, - PADDLE_SPORTS, + SOCIAL_DANCE, + STAIRS, + STEP_TRAINING, SURFING_SPORTS, + TAI_CHI, + TRACK_AND_FIELD, + TRADITIONAL_STRENGTH_TRAINING, WATER_FITNESS, WATER_SPORTS, - TAI_CHI, + WHEELCHAIR_RUN_PACE, + WHEELCHAIR_WALK_PACE, WRESTLING, // Android only - AEROBICS, - BIATHLON, - BIKING_HAND, - BIKING_MOUNTAIN, - BIKING_ROAD, - BIKING_SPINNING, BIKING_STATIONARY, - BIKING_UTILITY, CALISTHENICS, - CIRCUIT_TRAINING, - CROSS_FIT, DANCING, - DIVING, - ELEVATOR, - ERGOMETER, - ESCALATOR, FRISBEE_DISC, - GARDENING, GUIDED_BREATHING, - HORSEBACK_RIDING, - HOUSEWORK, - INTERVAL_TRAINING, - IN_VEHICLE, ICE_SKATING, - KAYAKING, - KETTLEBELL_TRAINING, - KICK_SCOOTER, - KITE_SURFING, - MEDITATION, - MIXED_MARTIAL_ARTS, - P90X, PARAGLIDING, - POLO, ROCK_CLIMBING, // on iOS this is the same as CLIMBING ROWING_MACHINE, - RUNNING_JOGGING, // on iOS this is the same as RUNNING - RUNNING_SAND, // on iOS this is the same as RUNNING RUNNING_TREADMILL, // on iOS this is the same as RUNNING SCUBA_DIVING, - SKATING_CROSS, // on iOS this is the same as SKATING - SKATING_INDOOR, // on iOS this is the same as SKATING - SKATING_INLINE, // on iOS this is the same as SKATING SKIING, - SKIING_BACK_COUNTRY, - SKIING_KITE, - SKIING_ROLLER, - SLEDDING, - SNOWMOBILE, SNOWSHOEING, STAIR_CLIMBING_MACHINE, - STANDUP_PADDLEBOARDING, - STILL, STRENGTH_TRAINING, SURFING, SWIMMING_OPEN_WATER, SWIMMING_POOL, - TEAM_SPORTS, - TILTING, - VOLLEYBALL_BEACH, - VOLLEYBALL_INDOOR, - WAKEBOARDING, - WALKING_FITNESS, - WALKING_NORDIC, - WALKING_STROLLER, WALKING_TREADMILL, WEIGHTLIFTING, WHEELCHAIR, - WINDSURFING, - ZUMBA, // OTHER, diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index cc118c503..dbfe4d751 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -1,5 +1,5 @@ name: health -description: Wrapper for HealthKit on iOS and Google Fit and Health Connect on Android. +description: Wrapper for Apple's HealthKit on iOS and Google's Health Connect on Android. version: 10.2.0 homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages/health From d1a89123ff5d2e1443ea0a527ce050e21fb8737f Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Thu, 22 Aug 2024 13:49:47 +0200 Subject: [PATCH 22/80] [Health] Combine certain workout types for consistency across iOS and Android (#1020) * Remove Google Fit and imports from Android code * Formatting * Remove Google Fit column from readme * Remove support for Google Fit types not supported by Health Connect * Remove more Google Fit workout types * Remove references to Google Fit, remove `useHealthConnectIfAvailable` * Remove `disconect` method channel * Remove `flowRate` from `writeBloodOxygen` as it is not supported in Health Connect * Remove more unsupported workout types * Add missing import * Remove Google Fit as dependency * Add notice in README * Improve logging for HC permission callback * Update some documentation * Android: Fix `requestAuthorization` not returning a result on success * Combine some types for consistency across iOS and Android --- packages/health/README.md | 190 +++++++++--------- .../cachet/plugins/health/HealthPlugin.kt | 16 +- .../ios/Classes/SwiftHealthPlugin.swift | 4 +- packages/health/lib/health.g.dart | 3 +- packages/health/lib/src/health_plugin.dart | 8 +- packages/health/lib/src/heath_data_types.dart | 3 +- 6 files changed, 121 insertions(+), 103 deletions(-) diff --git a/packages/health/README.md b/packages/health/README.md index ccd2d913c..c18e7e729 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -329,98 +329,98 @@ The plugin supports the following [`HealthDataType`](https://pub.dev/documentati The plugin supports the following [`HealthWorkoutActivityType`](https://pub.dev/documentation/health/latest/health/HealthWorkoutActivityType.html). -| **Workout Type** | **Apple Health** | **Google Health Connect** | **Comments** | -| -------------------------------- | ---------------- | ------------------------- | ----------------------------------------------------------------- | -| AMERICAN_FOOTBALL | yes | yes | | -| ARCHERY | yes | | | -| AUSTRALIAN_FOOTBALL | yes | yes | | -| BADMINTON | yes | yes | | -| BARRE | yes | | | -| BASEBALL | yes | yes | | -| BASKETBALL | yes | yes | | -| BIKING | yes | yes | on iOS this is CYCLING, but name changed here to fit with Android | -| BOWLING | yes | | | -| BOXING | yes | yes | | -| CALISTHENICS | | yes | | -| CARDIO_DANCE | yes | | | -| CLIMBING | yes | | | -| COOLDOWN | yes | | | -| CORE_TRAINING | yes | | | -| CRICKET | yes | yes | | -| CROSS_COUNTRY_SKIING | yes | | | -| CROSS_TRAINING | yes | | | -| CURLING | yes | | | -| DANCING | yes | yes | on iOS this is DANCE, but name changed here to fit with Android | -| DISC_SPORTS | yes | | | -| DOWNHILL_SKIING | yes | | | -| ELLIPTICAL | yes | yes | | -| EQUESTRIAN_SPORTS | yes | | | -| FENCING | yes | yes | | -| FISHING | yes | | | -| FITNESS_GAMING | yes | | | -| FLEXIBILITY | yes | | | -| FRISBEE_DISC | | yes | | -| FUNCTIONAL_STRENGTH_TRAINING | yes | | | -| GOLF | yes | yes | | -| GUIDED_BREATHING | | yes | | -| GYMNASTICS | yes | yes | | -| HAND_CYCLING | yes | | | -| HANDBALL | yes | yes | | -| HIGH_INTENSITY_INTERVAL_TRAINING | yes | yes | | -| HIKING | yes | yes | | -| HOCKEY | yes | | | -| HUNTING | yes | | | -| JUMP_ROPE | yes | | | -| KICKBOXING | yes | | | -| LACROSSE | yes | | | -| MARTIAL_ARTS | yes | yes | | -| MIND_AND_BODY | yes | | | -| MIXED_CARDIO | yes | | | -| PADDLE_SPORTS | yes | | | -| PARAGLIDING | | yes | | -| PICKLEBALL | yes | | | -| PILATES | yes | yes | | -| PLAY | yes | | | -| PREPARATION_AND_RECOVERY | yes | | | -| RACQUETBALL | yes | yes | | -| ROCK_CLIMBING | (yes) | yes | on iOS this will be stored as CLIMBING | -| ROWING | yes | yes | | -| RUGBY | yes | yes | | -| RUNNING | yes | yes | | -| RUNNING_TREADMILL | (yes) | yes | on iOS this will be stored as RUNNING | -| SAILING | yes | yes | | -| SCUBA_DIVING | | yes | | -| SKATING | yes | yes | On iOS this is skating_sports | -| SNOW_SPORTS | yes | | | -| SNOWBOARDING | yes | yes | | -| SOCCER | yes | | | -| SOCIAL_DANCE | yes | | | -| SOFTBALL | yes | yes | | -| SQUASH | yes | yes | | -| STAIR_CLIMBING | yes | yes | | -| STAIR_CLIMBING_MACHINE | | yes | | -| STAIRS | yes | | | -| STEP_TRAINING | yes | | | -| STRENGTH_TRAINING | | yes | | -| SURFING | | yes | | -| SURFING_SPORTS | yes | | | -| SWIMMING | yes | | | -| SWIMMING_OPEN_WATER | | yes | | -| SWIMMING_POOL | | yes | | -| TABLE_TENNIS | yes | yes | | -| TAI_CHI | yes | | | -| TENNIS | yes | yes | | -| TRACK_AND_FIELD | yes | | | -| TRADITIONAL_STRENGTH_TRAINING | yes | | | -| VOLLEYBALL | yes | yes | | -| WALKING | yes | yes | | -| WATER_FITNESS | yes | | | -| WATER_POLO | yes | yes | | -| WATER_SPORTS | yes | | | -| WEIGHTLIFTING | | yes | | -| WHEELCHAIR | | yes | | -| WHEELCHAIR_RUN_PACE | yes | | | -| WHEELCHAIR_WALK_PACE | yes | | | -| WRESTLING | yes | | | -| YOGA | yes | yes | | -| OTHER | yes | yes | | +| **Workout Type** | **Apple Health** | **Google Health Connect** | **Comments** | +| -------------------------------- | ---------------- | ------------------------- | ----------------------------------------------------------------------------------------------- | +| AMERICAN_FOOTBALL | yes | yes | | +| ARCHERY | yes | | | +| AUSTRALIAN_FOOTBALL | yes | yes | | +| BADMINTON | yes | yes | | +| BARRE | yes | | | +| BASEBALL | yes | yes | | +| BASKETBALL | yes | yes | | +| BIKING | yes | yes | on iOS this is CYCLING, but name changed here to fit with Android | +| BOWLING | yes | | | +| BOXING | yes | yes | | +| CALISTHENICS | | yes | | +| CARDIO_DANCE | yes | (yes) | on Android this will be stored as DANCING | +| CLIMBING | yes | | | +| COOLDOWN | yes | | | +| CORE_TRAINING | yes | | | +| CRICKET | yes | yes | | +| CROSS_COUNTRY_SKIING | yes | (yes) | on Android this will be stored as SKIING | +| CROSS_TRAINING | yes | | | +| CURLING | yes | | | +| DANCING | yes | yes | on iOS this is DANCE, but name changed here to fit with Android | +| DISC_SPORTS | yes | | | +| DOWNHILL_SKIING | yes | (yes) | on Android this will be stored as SKIING | +| ELLIPTICAL | yes | yes | | +| EQUESTRIAN_SPORTS | yes | | | +| FENCING | yes | yes | | +| FISHING | yes | | | +| FITNESS_GAMING | yes | | | +| FLEXIBILITY | yes | | | +| FRISBEE_DISC | | yes | | +| FUNCTIONAL_STRENGTH_TRAINING | yes | (yes) | on Android this will be stored as STRENGTH_TRAINING | +| GOLF | yes | yes | | +| GUIDED_BREATHING | | yes | | +| GYMNASTICS | yes | yes | | +| HAND_CYCLING | yes | | | +| HANDBALL | yes | yes | | +| HIGH_INTENSITY_INTERVAL_TRAINING | yes | yes | | +| HIKING | yes | yes | | +| HOCKEY | yes | | | +| HUNTING | yes | | | +| JUMP_ROPE | yes | | | +| KICKBOXING | yes | | | +| LACROSSE | yes | | | +| MARTIAL_ARTS | yes | yes | | +| MIND_AND_BODY | yes | | | +| MIXED_CARDIO | yes | | | +| PADDLE_SPORTS | yes | | | +| PARAGLIDING | | yes | | +| PICKLEBALL | yes | | | +| PILATES | yes | yes | | +| PLAY | yes | | | +| PREPARATION_AND_RECOVERY | yes | | | +| RACQUETBALL | yes | yes | | +| ROCK_CLIMBING | (yes) | yes | on iOS this will be stored as CLIMBING | +| ROWING | yes | yes | | +| RUGBY | yes | yes | | +| RUNNING | yes | yes | | +| RUNNING_TREADMILL | (yes) | yes | on iOS this will be stored as RUNNING | +| SAILING | yes | yes | | +| SCUBA_DIVING | | yes | | +| SKATING | yes | yes | On iOS this will be stored as SKATING_SPORTS | +| SKIING | (yes) | yes | on iOS you have to choose between CROSS_COUNTRY_SKIING and DOWNHILL_SKIING | +| SNOW_SPORTS | yes | | | +| SNOWBOARDING | yes | yes | | +| SOCCER | yes | | | +| SOCIAL_DANCE | yes | (yes) | on Android this will be stored as DANCING | +| SOFTBALL | yes | yes | | +| SQUASH | yes | yes | | +| STAIR_CLIMBING | yes | yes | | +| STAIR_CLIMBING_MACHINE | | yes | | +| STAIRS | yes | | | +| STEP_TRAINING | yes | | | +| STRENGTH_TRAINING | (yes) | yes | on iOS you have to choose between FUNCTIONAL_STRENGTH_TRAINING or TRADITIONAL_STRENGTH_TRAINING | +| SURFING | yes | yes | on iOS this is SURFING_SPORTS, but name changed here to fit with Android | +| SWIMMING | yes | (yes) | on Android you have to choose between SWIMMING_OPEN_WATER and SWIMMING_POOL | +| SWIMMING_OPEN_WATER | (yes) | yes | on iOS this will be stored as SWIMMING | +| SWIMMING_POOL | (yes) | yes | on iOS this will be stored as SWIMMING | +| TABLE_TENNIS | yes | yes | | +| TAI_CHI | yes | | | +| TENNIS | yes | yes | | +| TRACK_AND_FIELD | yes | | | +| TRADITIONAL_STRENGTH_TRAINING | yes | (yes) | on Android this will be stored as STRENGTH_TRAINING | +| VOLLEYBALL | yes | yes | | +| WALKING | yes | yes | | +| WATER_FITNESS | yes | | | +| WATER_POLO | yes | yes | | +| WATER_SPORTS | yes | | | +| WEIGHTLIFTING | | yes | | +| WHEELCHAIR | (yes) | yes | on iOS you have to choose between WHEELCHAIR_RUN_PACE or WHEELCHAIR_WALK_PACE | +| WHEELCHAIR_RUN_PACE | yes | (yes) | on Android this will be stored as WHEELCHAIR | +| WHEELCHAIR_WALK_PACE | yes | (yes) | on Android this will be stored as WHEELCHAIR | +| WRESTLING | yes | | | +| YOGA | yes | yes | | +| OTHER | yes | yes | | diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index b80b2b53d..a4761f48f 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -2203,10 +2203,13 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "CALISTHENICS" to ExerciseSessionRecord .EXERCISE_TYPE_CALISTHENICS, + "CARDIO_DANCE" to + ExerciseSessionRecord + .EXERCISE_TYPE_DANCING, "CRICKET" to ExerciseSessionRecord.EXERCISE_TYPE_CRICKET, - // "CROSS_COUNTRY_SKIING" to ExerciseSessionRecord.EXERCISE_TYPE_SKIING_CROSS_COUNTRY, + "CROSS_COUNTRY_SKIING" to ExerciseSessionRecord.EXERCISE_TYPE_SKIING, "DANCING" to ExerciseSessionRecord.EXERCISE_TYPE_DANCING, - // "DOWNHILL_SKIING" to ExerciseSessionRecord.EXERCISE_TYPE_SKIING_DOWNHILL, + "DOWNHILL_SKIING" to ExerciseSessionRecord.EXERCISE_TYPE_SKIING, "ELLIPTICAL" to ExerciseSessionRecord .EXERCISE_TYPE_ELLIPTICAL, @@ -2265,6 +2268,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ExerciseSessionRecord .EXERCISE_TYPE_SNOWSHOEING, // "SOCCER" to ExerciseSessionRecord.EXERCISE_TYPE_FOOTBALL_SOCCER, + "SOCIAL_DANCE" to + ExerciseSessionRecord + .EXERCISE_TYPE_DANCING, "SOFTBALL" to ExerciseSessionRecord.EXERCISE_TYPE_SOFTBALL, "SQUASH" to ExerciseSessionRecord.EXERCISE_TYPE_SQUASH, "STAIR_CLIMBING_MACHINE" to @@ -2300,6 +2306,12 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "WHEELCHAIR" to ExerciseSessionRecord .EXERCISE_TYPE_WHEELCHAIR, + "WHEELCHAIR_RUN_PACE" to + ExerciseSessionRecord + .EXERCISE_TYPE_WHEELCHAIR, + "WHEELCHAIR_WALK_PACE" to + ExerciseSessionRecord + .EXERCISE_TYPE_WHEELCHAIR, "YOGA" to ExerciseSessionRecord.EXERCISE_TYPE_YOGA, "OTHER" to ExerciseSessionRecord.EXERCISE_TYPE_OTHER_WORKOUT, ) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 9b00a3c2b..19d8cc207 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -1339,8 +1339,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { workoutActivityTypeMap["PADDLE_SPORTS"] = .paddleSports workoutActivityTypeMap["ROWING"] = .rowing workoutActivityTypeMap["SAILING"] = .sailing - workoutActivityTypeMap["SURFING_SPORTS"] = .surfingSports + workoutActivityTypeMap["SURFING"] = .surfingSports workoutActivityTypeMap["SWIMMING"] = .swimming + workoutActivityTypeMap["SWIMMING_OPEN_WATER"] = .swimming + workoutActivityTypeMap["SWIMMING_POOL"] = .swimming workoutActivityTypeMap["WATER_FITNESS"] = .waterFitness workoutActivityTypeMap["WATER_POLO"] = .waterPolo workoutActivityTypeMap["WATER_SPORTS"] = .waterSports diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart index 104be599d..7740670ae 100644 --- a/packages/health/lib/health.g.dart +++ b/packages/health/lib/health.g.dart @@ -385,7 +385,7 @@ const _$HealthWorkoutActivityTypeEnumMap = { HealthWorkoutActivityType.SOCIAL_DANCE: 'SOCIAL_DANCE', HealthWorkoutActivityType.STAIRS: 'STAIRS', HealthWorkoutActivityType.STEP_TRAINING: 'STEP_TRAINING', - HealthWorkoutActivityType.SURFING_SPORTS: 'SURFING_SPORTS', + HealthWorkoutActivityType.SURFING: 'SURFING', HealthWorkoutActivityType.TAI_CHI: 'TAI_CHI', HealthWorkoutActivityType.TRACK_AND_FIELD: 'TRACK_AND_FIELD', HealthWorkoutActivityType.TRADITIONAL_STRENGTH_TRAINING: @@ -410,7 +410,6 @@ const _$HealthWorkoutActivityTypeEnumMap = { HealthWorkoutActivityType.SNOWSHOEING: 'SNOWSHOEING', HealthWorkoutActivityType.STAIR_CLIMBING_MACHINE: 'STAIR_CLIMBING_MACHINE', HealthWorkoutActivityType.STRENGTH_TRAINING: 'STRENGTH_TRAINING', - HealthWorkoutActivityType.SURFING: 'SURFING', HealthWorkoutActivityType.SWIMMING_OPEN_WATER: 'SWIMMING_OPEN_WATER', HealthWorkoutActivityType.SWIMMING_POOL: 'SWIMMING_POOL', HealthWorkoutActivityType.WALKING_TREADMILL: 'WALKING_TREADMILL', diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index a55bc49e4..a6232e202 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -1123,7 +1123,7 @@ class Health { HealthWorkoutActivityType.STAIR_CLIMBING, HealthWorkoutActivityType.STAIRS, HealthWorkoutActivityType.STEP_TRAINING, - HealthWorkoutActivityType.SURFING_SPORTS, + HealthWorkoutActivityType.SURFING, HealthWorkoutActivityType.SWIMMING, HealthWorkoutActivityType.TABLE_TENNIS, HealthWorkoutActivityType.TAI_CHI, @@ -1139,6 +1139,8 @@ class Health { HealthWorkoutActivityType.WHEELCHAIR_WALK_PACE, HealthWorkoutActivityType.WRESTLING, HealthWorkoutActivityType.YOGA, + HealthWorkoutActivityType.SWIMMING_OPEN_WATER, + HealthWorkoutActivityType.SWIMMING_POOL, }.contains(type); } @@ -1155,6 +1157,7 @@ class Health { HealthWorkoutActivityType.BASKETBALL, HealthWorkoutActivityType.BIKING, HealthWorkoutActivityType.BOXING, + HealthWorkoutActivityType.CARDIO_DANCE, HealthWorkoutActivityType.CRICKET, HealthWorkoutActivityType.CROSS_COUNTRY_SKIING, HealthWorkoutActivityType.CURLING, @@ -1177,6 +1180,7 @@ class Health { HealthWorkoutActivityType.SKATING, HealthWorkoutActivityType.SNOWBOARDING, HealthWorkoutActivityType.SOCCER, + HealthWorkoutActivityType.SOCIAL_DANCE, HealthWorkoutActivityType.SOFTBALL, HealthWorkoutActivityType.SQUASH, HealthWorkoutActivityType.STAIR_CLIMBING, @@ -1185,6 +1189,8 @@ class Health { HealthWorkoutActivityType.VOLLEYBALL, HealthWorkoutActivityType.WALKING, HealthWorkoutActivityType.WATER_POLO, + HealthWorkoutActivityType.WHEELCHAIR_RUN_PACE, + HealthWorkoutActivityType.WHEELCHAIR_WALK_PACE, HealthWorkoutActivityType.YOGA, // Android only diff --git a/packages/health/lib/src/heath_data_types.dart b/packages/health/lib/src/heath_data_types.dart index 014cfe1da..c498de3a1 100644 --- a/packages/health/lib/src/heath_data_types.dart +++ b/packages/health/lib/src/heath_data_types.dart @@ -518,7 +518,7 @@ enum HealthWorkoutActivityType { SOCIAL_DANCE, STAIRS, STEP_TRAINING, - SURFING_SPORTS, + SURFING, TAI_CHI, TRACK_AND_FIELD, TRADITIONAL_STRENGTH_TRAINING, @@ -544,7 +544,6 @@ enum HealthWorkoutActivityType { SNOWSHOEING, STAIR_CLIMBING_MACHINE, STRENGTH_TRAINING, - SURFING, SWIMMING_OPEN_WATER, SWIMMING_POOL, WALKING_TREADMILL, From b70d1d7a5accdb019c9c3d8a54f8a53e5628913e Mon Sep 17 00:00:00 2001 From: Panagiotis Giannoutsos <36935711+Panosfunk@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:37:18 +0200 Subject: [PATCH 23/80] Update README.md --- packages/health/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/health/README.md b/packages/health/README.md index c18e7e729..12f71cbdc 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -2,7 +2,7 @@ Enables reading and writing health data from/to Apple Health and Health Connect. -> **NOTE:** Google has deprecated the Google Fit API. According to the [documentation](https://developers.google.com/fit/android), as of **May 1st 2014** developers cannot sign up for using the API. As such, this package has removed support for Google Fit as of version 11.0.0 and users are urged to upgrade as soon as possible. +> **NOTE:** Google has deprecated the Google Fit API. According to the [documentation](https://developers.google.com/fit/android), as of **May 1st 2024** developers cannot sign up for using the API. As such, this package has removed support for Google Fit as of version 11.0.0 and users are urged to upgrade as soon as possible. The plugin supports: From 4b19432f8717317cb3567bee09c12a2d3d9d2b02 Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Thu, 29 Aug 2024 16:18:06 +0200 Subject: [PATCH 24/80] [Health] Add UUID field to `HealthDataPoint` (#1019) * Remove Google Fit and imports from Android code * Formatting * Remove Google Fit column from readme * Remove support for Google Fit types not supported by Health Connect * Remove more Google Fit workout types * Remove references to Google Fit, remove `useHealthConnectIfAvailable` * Remove `disconect` method channel * Remove `flowRate` from `writeBloodOxygen` as it is not supported in Health Connect * Remove more unsupported workout types * Add missing import * Remove Google Fit as dependency * Add notice in README * Improve logging for HC permission callback * Update some documentation * Android: Fix `requestAuthorization` not returning a result on success * Remove additional workout types that are not supported * Remove another workout type * Add missing unimplemented method * Add `uuid` field to `HealthDataPoint` and include it in datapoints * Update README.md --------- Co-authored-by: bardram --- packages/health/README.md | 1 + .../cachet/plugins/health/HealthPlugin.kt | 52 +++++++++++++++++-- packages/health/lib/health.g.dart | 2 + .../health/lib/src/health_data_point.dart | 10 +++- packages/health/lib/src/health_plugin.dart | 1 + 5 files changed, 62 insertions(+), 4 deletions(-) diff --git a/packages/health/README.md b/packages/health/README.md index 12f71cbdc..bbf0cd1fe 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -191,6 +191,7 @@ Below is a simplified flow of how to use the plugin. A [`HealthDataPoint`](https://pub.dev/documentation/health/latest/health/HealthDataPoint-class.html) object contains the following data fields: ```dart +String uuid; HealthValue value; HealthDataType type; HealthDataUnit unit; diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index a4761f48f..844d350f6 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -18,6 +18,7 @@ import androidx.health.connect.client.records.MealType.MEAL_TYPE_DINNER import androidx.health.connect.client.records.MealType.MEAL_TYPE_LUNCH import androidx.health.connect.client.records.MealType.MEAL_TYPE_SNACK import androidx.health.connect.client.records.MealType.MEAL_TYPE_UNKNOWN +import androidx.health.connect.client.records.metadata.Metadata import androidx.health.connect.client.request.AggregateGroupByDurationRequest import androidx.health.connect.client.request.AggregateRequest import androidx.health.connect.client.request.ReadRecordsRequest @@ -743,6 +744,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : healthConnectData.add( // mapOf( mapOf( + "uuid" to record.metadata.id, "workoutActivityType" to (workoutTypeMap .filterValues { @@ -815,8 +817,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : convertRecordStage( recStage, dataType, - rec.metadata.dataOrigin - .packageName + rec.metadata ) ) } @@ -847,10 +848,13 @@ class HealthPlugin(private var channel: MethodChannel? = null) : private fun convertRecordStage( stage: SleepSessionRecord.Stage, dataType: String, - sourceName: String + metadata: Metadata ): List> { + var sourceName = metadata.dataOrigin + .packageName return listOf( mapOf( + "uuid" to metadata.id, "stage" to stage.stage, "value" to ChronoUnit.MINUTES.between( @@ -946,6 +950,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is WeightRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.weight .inKilograms, @@ -965,6 +971,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is HeightRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.height .inMeters, @@ -984,6 +992,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is BodyFatRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.percentage .value, @@ -1003,6 +1013,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is StepsRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.count, "date_from" to record.startTime @@ -1020,6 +1032,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is ActiveCaloriesBurnedRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.energy .inKilocalories, @@ -1039,6 +1053,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is HeartRateRecord -> return record.samples.map { mapOf( + "uuid" to + metadata.id, "value" to it.beatsPerMinute, "date_from" to it.time.toEpochMilli(), @@ -1053,6 +1069,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is HeartRateVariabilityRmssdRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.heartRateVariabilityMillis, "date_from" to @@ -1071,6 +1089,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is BodyTemperatureRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.temperature .inCelsius, @@ -1090,6 +1110,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is BodyWaterMassRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.mass .inKilograms, @@ -1109,6 +1131,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is BloodPressureRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to if (dataType == BLOOD_PRESSURE_DIASTOLIC @@ -1134,6 +1158,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is OxygenSaturationRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.percentage .value, @@ -1153,6 +1179,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is BloodGlucoseRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.level .inMilligramsPerDeciliter, @@ -1172,6 +1200,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is DistanceRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.distance .inMeters, @@ -1191,6 +1221,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is HydrationRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.volume .inLiters, @@ -1210,6 +1242,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is TotalCaloriesBurnedRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.energy .inKilocalories, @@ -1229,6 +1263,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is BasalMetabolicRateRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.basalMetabolicRate .inKilocaloriesPerDay, @@ -1248,6 +1284,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is SleepSessionRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "date_from" to record.startTime .toEpochMilli(), @@ -1270,6 +1308,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is RestingHeartRateRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.beatsPerMinute, "date_from" to @@ -1288,6 +1328,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is FloorsClimbedRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.floors, "date_from" to record.startTime @@ -1305,6 +1347,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is RespiratoryRateRecord -> return listOf( mapOf( + "uuid" to + metadata.id, "value" to record.rate, "date_from" to record.time @@ -1322,6 +1366,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is NutritionRecord -> return listOf( mapOf( + "uuid" to metadata.id, "calories" to record.energy?.inKilocalories, "protein" to record.protein?.inGrams, "carbs" to record.totalCarbohydrate?.inGrams, @@ -1385,6 +1430,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : is MenstruationFlowRecord -> return listOf( mapOf( + "uuid" to metadata.id, "value" to record.flow, "date_from" to record.time.toEpochMilli(), "date_to" to record.time.toEpochMilli(), diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart index 7740670ae..13f14c5a6 100644 --- a/packages/health/lib/health.g.dart +++ b/packages/health/lib/health.g.dart @@ -8,6 +8,7 @@ part of 'health.dart'; HealthDataPoint _$HealthDataPointFromJson(Map json) => HealthDataPoint( + uuid: json['uuid'] as String, value: HealthValue.fromJson(json['value'] as Map), type: $enumDecode(_$HealthDataTypeEnumMap, json['type']), unit: $enumDecode(_$HealthDataUnitEnumMap, json['unit']), @@ -28,6 +29,7 @@ HealthDataPoint _$HealthDataPointFromJson(Map json) => Map _$HealthDataPointToJson(HealthDataPoint instance) { final val = { + 'uuid': instance.uuid, 'value': instance.value, 'type': _$HealthDataTypeEnumMap[instance.type]!, 'unit': _$HealthDataUnitEnumMap[instance.unit]!, diff --git a/packages/health/lib/src/health_data_point.dart b/packages/health/lib/src/health_data_point.dart index b98734c5a..ec5a98a9c 100644 --- a/packages/health/lib/src/health_data_point.dart +++ b/packages/health/lib/src/health_data_point.dart @@ -8,6 +8,9 @@ enum HealthPlatformType { appleHealth, googleHealthConnect } /// as value. @JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) class HealthDataPoint { + /// UUID of the data point. + String uuid; + /// The quantity value of the data point HealthValue value; @@ -51,6 +54,7 @@ class HealthDataPoint { Map? metadata; HealthDataPoint({ + required this.uuid, required this.value, required this.type, required this.unit, @@ -129,6 +133,7 @@ class HealthDataPoint { ? null : Map.from(dataPoint['metadata'] as Map); final unit = dataTypeToUnit[dataType] ?? HealthDataUnit.UNKNOWN_UNIT; + final String? uuid = dataPoint["uuid"] as String?; // Set WorkoutSummary, if available. WorkoutSummary? workoutSummary; @@ -140,6 +145,7 @@ class HealthDataPoint { } return HealthDataPoint( + uuid: uuid ?? "", value: value, type: dataType, unit: unit, @@ -157,6 +163,7 @@ class HealthDataPoint { @override String toString() => """$runtimeType - + uuid: $uuid, value: ${value.toString()}, unit: ${unit.name}, dateFrom: $dateFrom, @@ -173,6 +180,7 @@ class HealthDataPoint { @override bool operator ==(Object other) => other is HealthDataPoint && + uuid == other.uuid && value == other.value && unit == other.unit && dateFrom == other.dateFrom && @@ -186,6 +194,6 @@ class HealthDataPoint { metadata == other.metadata; @override - int get hashCode => Object.hash(value, unit, dateFrom, dateTo, type, + int get hashCode => Object.hash(uuid, value, unit, dateFrom, dateTo, type, sourcePlatform, sourceDeviceId, sourceId, sourceName, metadata); } diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index a6232e202..9375e5c81 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -265,6 +265,7 @@ class Health { (weights[i].value as NumericHealthValue).numericValue.toDouble() / (h * h); final x = HealthDataPoint( + uuid: '', value: NumericHealthValue(numericValue: bmiValue), type: dataType, unit: unit, From c41ea4802094978a0c767a5243fad306208fc2ba Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Fri, 30 Aug 2024 10:31:48 +0200 Subject: [PATCH 25/80] [Health] Remove references to deprecated v1 Android embeddings (#1021) * Remove Google Fit and imports from Android code * Formatting * Remove Google Fit column from readme * Remove support for Google Fit types not supported by Health Connect * Remove more Google Fit workout types * Remove references to Google Fit, remove `useHealthConnectIfAvailable` * Remove `disconect` method channel * Remove `flowRate` from `writeBloodOxygen` as it is not supported in Health Connect * Remove more unsupported workout types * Add missing import * Remove Google Fit as dependency * Add notice in README * Improve logging for HC permission callback * Update some documentation * Android: Fix `requestAuthorization` not returning a result on success * Remove additional workout types that are not supported * Remove another workout type * Add missing unimplemented method * Remove reference to v1 Android embedding --- .../cachet/plugins/health/HealthPlugin.kt | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 844d350f6..c1a29602c 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -32,7 +32,6 @@ import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result import io.flutter.plugin.common.PluginRegistry.ActivityResultListener -import io.flutter.plugin.common.PluginRegistry.Registrar import java.time.* import java.time.temporal.ChronoUnit import java.util.* @@ -119,28 +118,6 @@ class HealthPlugin(private var channel: MethodChannel? = null) : threadPoolExecutor = null } - // This static function is optional and equivalent to onAttachedToEngine. It supports the - // old - // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting - // plugin registration via this function while apps migrate to use the new Android APIs - // post-flutter-1.12 via https://flutter.dev/go/android-project-migration. - // - // It is encouraged to share logic between onAttachedToEngine and registerWith to keep - // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be - // called - // depending on the user's project. onAttachedToEngine or registerWith must both be defined - // in the same class. - companion object { - @Suppress("unused") - @JvmStatic - fun registerWith(registrar: Registrar) { - val channel = MethodChannel(registrar.messenger(), CHANNEL_NAME) - val plugin = HealthPlugin(channel) - registrar.addActivityResultListener(plugin) - channel.setMethodCallHandler(plugin) - } - } - override fun success(p0: Any?) { handler?.post { mResult?.success(p0) } } From ce4d444dd15b926817d56361a6dba2861a203744 Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Fri, 30 Aug 2024 13:24:11 +0200 Subject: [PATCH 26/80] [Health]: Improve support for `RecordingMethod` on Health Connect and `HKWasUserEntered` on iOS (#1023) * Remove Google Fit and imports from Android code * Formatting * Remove Google Fit column from readme * Remove support for Google Fit types not supported by Health Connect * Remove more Google Fit workout types * Remove references to Google Fit, remove `useHealthConnectIfAvailable` * Remove `disconect` method channel * Remove `flowRate` from `writeBloodOxygen` as it is not supported in Health Connect * Remove more unsupported workout types * Add missing import * Remove Google Fit as dependency * Add notice in README * Improve logging for HC permission callback * Update some documentation * Android: Fix `requestAuthorization` not returning a result on success * Remove additional workout types that are not supported * Remove another workout type * Add missing unimplemented method * Include recording method from Android metadata in HealthDataPoint * Support writing data with custom recording method on Android * Improve RecordingMethod enum * Support filtering by recording method when fetching data * Fix `includeManualEntry` for `getTotalStepsInInterval` * Support recording method on iOS * Recording method when writing on iOS (WIP) * Fix filtering manual entries when fetching data on iOS * Rename variable in example app * Update documentation * Improvements to example app * Quick fix * Update documentation * Update iOS docs --- packages/health/README.md | 33 ++- .../cachet/plugins/health/HealthPlugin.kt | 238 +++++++++++++++++- packages/health/example/lib/main.dart | 232 ++++++++++++----- .../ios/Classes/SwiftHealthPlugin.swift | 77 ++++-- packages/health/lib/health.g.dart | 13 +- .../health/lib/src/health_data_point.dart | 17 +- packages/health/lib/src/health_plugin.dart | 114 +++++++-- .../health/lib/src/health_value_types.dart | 40 +++ 8 files changed, 642 insertions(+), 122 deletions(-) diff --git a/packages/health/README.md b/packages/health/README.md index bbf0cd1fe..1e47314a3 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -181,6 +181,11 @@ Below is a simplified flow of how to use the plugin. bool success = await Health().writeHealthData(10, HealthDataType.STEPS, now, now); success = await Health().writeHealthData(3.1, HealthDataType.BLOOD_GLUCOSE, now, now); + // you can also specify the recording method to store in the metadata (default is RecordingMethod.automatic) + // on iOS only `RecordingMethod.automatic` and `RecordingMethod.manual` are supported + // Android additionally supports `RecordingMethod.active` and `RecordingMethod.unknown` + success &= await Health().writeHealthData(10, HealthDataType.STEPS, now, now, recordingMethod: RecordingMethod.manual); + // get the number of steps for today var midnight = DateTime(now.year, now.month, now.day); int? steps = await Health().getTotalStepsInInterval(midnight, now); @@ -201,7 +206,7 @@ HealthPlatformType sourcePlatform; String sourceDeviceId; String sourceId; String sourceName; -bool isManualEntry; +RecordingMethod recordingMethod; WorkoutSummary? workoutSummary; ``` @@ -223,7 +228,7 @@ A `HealthDataPoint` object can be serialized to and from JSON using the `toJson( "source_device_id": "F74938B9-C011-4DE4-AA5E-CF41B60B96E7", "source_id": "com.apple.health.81AE7156-EC05-47E3-AC93-2D6F65C717DF", "source_name": "iPhone12.bardram.net", - "is_manual_entry": false + "recording_method": 3 "value": { "__type": "NumericHealthValue", "numeric_value": 141.0 @@ -236,7 +241,7 @@ A `HealthDataPoint` object can be serialized to and from JSON using the `toJson( "source_device_id": "F74938B9-C011-4DE4-AA5E-CF41B60B96E7", "source_id": "com.apple.health.81AE7156-EC05-47E3-AC93-2D6F65C717DF", "source_name": "iPhone12.bardram.net", - "is_manual_entry": false + "recording_method": 2 } ``` @@ -251,6 +256,28 @@ flutter: Health Plugin Error: flutter: PlatformException(FlutterHealth, Results are null, Optional(Error Domain=com.apple.healthkit Code=6 "Protected health data is inaccessible" UserInfo={NSLocalizedDescription=Protected health data is inaccessible})) ``` +### Filtering by recording method +Google Health Connect and Apple HealthKit both provide ways to distinguish samples collected "automatically" and manually entered data by the user. + +- Android provides an enum with 4 variations: https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/metadata/Metadata#summary +- iOS has a boolean value: https://developer.apple.com/documentation/healthkit/hkmetadatakeywasuserentered + +As such, when fetching data you have the option to filter the fetched data by recording method as such: + +```dart +List healthData = await Health().getHealthDataFromTypes( + types: types, + startTime: yesterday, + endTime: now, + recordingMethodsToFilter: [RecordingMethod.manual, RecordingMethod.unknown], +); +``` + +**Note that for this to work, the information needs to have been provided when writing the data to Health Connect or Apple Health**. For example, steps added manually through the Apple Health App will set `HKWasUserEntered` to true (corresponding to `RecordingMethod.manual`), however it seems that adding steps manually to Google Fit does not write the data with the `RecordingMethod.manual` in the metadata, instead it shows up as `RecordingMethod.unknown`. This is an open issue, and as such filtering manual entries when querying step count on Android with `getTotalStepsInInterval(includeManualEntries: false)` does not necessarily filter out manual steps. + +**NOTE**: On iOS, you can only filter by `RecordingMethod.automatic` and `RecordingMethod`.manual` as it is stored `HKMetadataKeyWasUserEntered` is a boolean value in the metadata. + + ### Filtering out duplicates If the same data is requested multiple times and saved in the same array duplicates will occur. diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index c1a29602c..043c49f42 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -392,17 +392,26 @@ class HealthPlugin(private var channel: MethodChannel? = null) : private fun getTotalStepsInInterval(call: MethodCall, result: Result) { val start = call.argument("startTime")!! val end = call.argument("endTime")!! + val recordingMethodsToFilter = call.argument>("recordingMethodsToFilter")!! + if (recordingMethodsToFilter.isEmpty()) { + getAggregatedStepCount(start, end, result) + } else { + getStepCountFiltered(start, end, recordingMethodsToFilter, result) + } + } + + private fun getAggregatedStepCount(start: Long, end: Long, result: Result) { + val startInstant = Instant.ofEpochMilli(start) + val endInstant = Instant.ofEpochMilli(end) scope.launch { try { - val startInstant = Instant.ofEpochMilli(start) - val endInstant = Instant.ofEpochMilli(end) val response = healthConnectClient.aggregate( AggregateRequest( metrics = setOf( - StepsRecord.COUNT_TOTAL + StepsRecord.COUNT_TOTAL, ), timeRangeFilter = TimeRangeFilter.between( @@ -415,6 +424,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : // time range. val stepsInInterval = response[StepsRecord.COUNT_TOTAL] ?: 0L + Log.i( "FLUTTER_HEALTH::SUCCESS", "returning $stepsInInterval steps" @@ -431,6 +441,40 @@ class HealthPlugin(private var channel: MethodChannel? = null) : } } + /** get the step records manually and filter out manual entries **/ + private fun getStepCountFiltered(start: Long, end: Long, recordingMethodsToFilter: List, result: Result) { + scope.launch { + try { + val request = + ReadRecordsRequest( + recordType = StepsRecord::class, + timeRangeFilter = + TimeRangeFilter.between( + Instant.ofEpochMilli(start), + Instant.ofEpochMilli(end) + ), + ) + val response = healthConnectClient.readRecords(request) + val filteredRecords = filterRecordsByRecordingMethods( + recordingMethodsToFilter, + response.records + ) + val totalSteps = filteredRecords.sumOf { (it as StepsRecord).count.toInt() } + Log.i( + "FLUTTER_HEALTH::SUCCESS", + "returning $totalSteps steps (excluding manual entries)" + ) + result.success(totalSteps) + } catch (e: Exception) { + Log.e( + "FLUTTER_HEALTH::ERROR", + "Unable to return steps due to the following exception:" + ) + Log.e("FLUTTER_HEALTH::ERROR", Log.getStackTraceString(e)) + result.success(null) + } + } + } private fun getHealthConnectSdkStatus(call: MethodCall, result: Result) { checkAvailability() @@ -442,6 +486,24 @@ class HealthPlugin(private var channel: MethodChannel? = null) : } result.success(healthConnectStatus) } + + /** Filter records by recording methods */ + private fun filterRecordsByRecordingMethods( + recordingMethodsToFilter: List, + records: List + ): List { + if (recordingMethodsToFilter.isEmpty()) { + return records + } + + return records.filter { record -> + Log.i( + "FLUTTER_HEALTH", + "Filtering record with recording method ${record.metadata.recordingMethod}, filtering by $recordingMethodsToFilter. Result: ${recordingMethodsToFilter.contains(record.metadata.recordingMethod)}" + ) + return@filter !recordingMethodsToFilter.contains(record.metadata.recordingMethod) + } + } private fun hasPermissions(call: MethodCall, result: Result) { val args = call.arguments as HashMap<*, *> @@ -611,6 +673,13 @@ class HealthPlugin(private var channel: MethodChannel? = null) : val startTime = Instant.ofEpochMilli(call.argument("startTime")!!) val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) val healthConnectData = mutableListOf>() + val recordingMethodsToFilter = call.argument>("recordingMethodsToFilter")!! + + Log.i( + "FLUTTER_HEALTH", + "Getting data for $dataType between $startTime and $endTime, filtering by $recordingMethodsToFilter" + ) + scope.launch { try { mapToType[dataType]?.let { classType -> @@ -658,7 +727,12 @@ class HealthPlugin(private var channel: MethodChannel? = null) : // Workout needs distance and total calories burned too if (dataType == WORKOUT) { - for (rec in records) { + var filteredRecords = filterRecordsByRecordingMethods( + recordingMethodsToFilter, + records + ) + + for (rec in filteredRecords) { val record = rec as ExerciseSessionRecord val distanceRequest = healthConnectClient.readRecords( @@ -774,7 +848,12 @@ class HealthPlugin(private var channel: MethodChannel? = null) : } // Filter sleep stages for requested stage } else if (classType == SleepSessionRecord::class) { - for (rec in response.records) { + val filteredRecords = filterRecordsByRecordingMethods( + recordingMethodsToFilter, + response.records + ) + + for (rec in filteredRecords) { if (rec is SleepSessionRecord) { if (dataType == SLEEP_SESSION) { healthConnectData.addAll( @@ -803,7 +882,11 @@ class HealthPlugin(private var channel: MethodChannel? = null) : } } } else { - for (rec in records) { + val filteredRecords = filterRecordsByRecordingMethods( + recordingMethodsToFilter, + records + ) + for (rec in filteredRecords) { healthConnectData.addAll( convertRecord(rec, dataType) ) @@ -942,6 +1025,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -963,6 +1048,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -984,6 +1071,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1003,6 +1092,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1024,6 +1115,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1040,6 +1133,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ) } @@ -1060,6 +1155,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1081,6 +1178,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1102,6 +1201,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1129,6 +1230,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1150,6 +1253,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1171,6 +1276,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1192,6 +1299,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1213,6 +1322,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1234,6 +1345,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1255,6 +1368,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1279,6 +1394,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ), ) @@ -1299,6 +1416,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ) ) @@ -1318,6 +1437,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ) ) @@ -1337,6 +1458,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ) ) @@ -1401,6 +1524,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ) ) @@ -1415,6 +1540,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) : "source_name" to metadata.dataOrigin .packageName, + "recording_method" to + metadata.recordingMethod ) ) // is ExerciseSessionRecord -> return listOf(mapOf("value" to , @@ -1437,6 +1564,13 @@ class HealthPlugin(private var channel: MethodChannel? = null) : val startTime = call.argument("startTime")!! val endTime = call.argument("endTime")!! val value = call.argument("value")!! + val recordingMethod = call.argument("recordingMethod")!! + + Log.i( + "FLUTTER_HEALTH", + "Writing data for $type between $startTime and $endTime, value: $value, recording method: $recordingMethod" + ) + val record = when (type) { BODY_FAT_PERCENTAGE -> @@ -1450,6 +1584,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : value ), zoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) HEIGHT -> @@ -1463,6 +1600,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : value ), zoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) WEIGHT -> @@ -1476,6 +1616,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : value ), zoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) STEPS -> @@ -1491,6 +1634,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : count = value.toLong(), startZoneOffset = null, endZoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) ACTIVE_ENERGY_BURNED -> @@ -1509,6 +1655,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ), startZoneOffset = null, endZoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) HEART_RATE -> @@ -1534,6 +1683,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ), startZoneOffset = null, endZoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) BODY_TEMPERATURE -> @@ -1547,6 +1699,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : value ), zoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) BODY_WATER_MASS -> @@ -1560,6 +1715,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : value ), zoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) BLOOD_OXYGEN -> @@ -1573,6 +1731,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : value ), zoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) BLOOD_GLUCOSE -> @@ -1586,6 +1747,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : value ), zoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) HEART_RATE_VARIABILITY_RMSSD -> @@ -1598,6 +1762,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : value, zoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) DISTANCE_DELTA -> @@ -1616,6 +1783,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ), startZoneOffset = null, endZoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) WATER -> @@ -1634,6 +1804,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ), startZoneOffset = null, endZoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) SLEEP_ASLEEP -> @@ -1662,6 +1835,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : .STAGE_TYPE_SLEEPING ) ), + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) SLEEP_LIGHT -> @@ -1690,6 +1866,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : .STAGE_TYPE_LIGHT ) ), + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) SLEEP_DEEP -> @@ -1718,6 +1897,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : .STAGE_TYPE_DEEP ) ), + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) SLEEP_REM -> @@ -1746,6 +1928,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : .STAGE_TYPE_REM ) ), + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) SLEEP_OUT_OF_BED -> @@ -1774,6 +1959,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : .STAGE_TYPE_OUT_OF_BED ) ), + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) SLEEP_AWAKE -> @@ -1802,6 +1990,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : .STAGE_TYPE_AWAKE ) ), + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) SLEEP_SESSION -> @@ -1816,6 +2007,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ), startZoneOffset = null, endZoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) RESTING_HEART_RATE -> @@ -1827,6 +2021,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : beatsPerMinute = value.toLong(), zoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) BASAL_ENERGY_BURNED -> @@ -1840,6 +2037,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : value ), zoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) FLIGHTS_CLIMBED -> @@ -1855,6 +2055,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : floors = value, startZoneOffset = null, endZoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) RESPIRATORY_RATE -> @@ -1865,6 +2068,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ), rate = value, zoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) // AGGREGATE_STEP_COUNT -> StepsRecord() TOTAL_CALORIES_BURNED -> @@ -1883,12 +2089,18 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ), startZoneOffset = null, endZoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) MENSTRUATION_FLOW -> MenstruationFlowRecord( time = Instant.ofEpochMilli(startTime), flow = value.toInt(), zoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ) BLOOD_PRESSURE_SYSTOLIC -> @@ -1933,6 +2145,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : val endTime = Instant.ofEpochMilli(call.argument("endTime")!!) val totalEnergyBurned = call.argument("totalEnergyBurned") val totalDistance = call.argument("totalDistance") + val recordingMethod = call.argument("recordingMethod")!! if (!workoutTypeMap.containsKey(type)) { result.success(false) Log.w( @@ -1955,6 +2168,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : endZoneOffset = null, exerciseType = workoutType, title = title, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ), ) if (totalDistance != null) { @@ -1968,6 +2184,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : Length.meters( totalDistance.toDouble() ), + metadata = Metadata( + recordingMethod = recordingMethod, + ), ), ) } @@ -1983,6 +2202,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : totalEnergyBurned .toDouble() ), + metadata = Metadata( + recordingMethod = recordingMethod, + ), ), ) } @@ -2011,6 +2233,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : val systolic = call.argument("systolic")!! val diastolic = call.argument("diastolic")!! val startTime = Instant.ofEpochMilli(call.argument("startTime")!!) + val recordingMethod = call.argument("recordingMethod")!! scope.launch { try { @@ -2027,6 +2250,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) : diastolic ), zoneOffset = null, + metadata = Metadata( + recordingMethod = recordingMethod, + ), ), ), ) diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index 3912bda41..038b4cf67 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -36,6 +36,7 @@ class _HealthAppState extends State { List _healthDataList = []; AppState _state = AppState.DATA_NOT_FETCHED; int _nofSteps = 0; + List recordingMethodsToFilter = []; // All types available depending on platform (iOS ot Android). List get types => (Platform.isAndroid) @@ -153,11 +154,15 @@ class _HealthAppState extends State { types: types, startTime: yesterday, endTime: now, + recordingMethodsToFilter: recordingMethodsToFilter, ); debugPrint('Total number of data points: ${healthData.length}. ' '${healthData.length > 100 ? 'Only showing the first 100.' : ''}'); + // sort the data points by date + healthData.sort((a, b) => b.dateTo.compareTo(a.dateTo)); + // save all the new data points (only the first 100) _healthDataList.addAll( (healthData.length < 100) ? healthData : healthData.sublist(0, 100)); @@ -194,29 +199,49 @@ class _HealthAppState extends State { value: 1.925, type: HealthDataType.HEIGHT, startTime: earlier, - endTime: now); + endTime: now, + recordingMethod: RecordingMethod.manual); success &= await Health().writeHealthData( - value: 90, type: HealthDataType.WEIGHT, startTime: now); + value: 90, + type: HealthDataType.WEIGHT, + startTime: now, + recordingMethod: RecordingMethod.manual); success &= await Health().writeHealthData( value: 90, type: HealthDataType.HEART_RATE, startTime: earlier, - endTime: now); + endTime: now, + recordingMethod: RecordingMethod.manual); success &= await Health().writeHealthData( value: 90, type: HealthDataType.STEPS, startTime: earlier, - endTime: now); + endTime: now, + recordingMethod: RecordingMethod.manual); success &= await Health().writeHealthData( - value: 200, - type: HealthDataType.ACTIVE_ENERGY_BURNED, - startTime: earlier, - endTime: now); + value: 200, + type: HealthDataType.ACTIVE_ENERGY_BURNED, + startTime: earlier, + endTime: now, + ); success &= await Health().writeHealthData( value: 70, type: HealthDataType.HEART_RATE, startTime: earlier, endTime: now); + if (Platform.isIOS) { + success &= await Health().writeHealthData( + value: 30, + type: HealthDataType.HEART_RATE_VARIABILITY_SDNN, + startTime: earlier, + endTime: now); + } else { + success &= await Health().writeHealthData( + value: 30, + type: HealthDataType.HEART_RATE_VARIABILITY_RMSSD, + startTime: earlier, + endTime: now); + } success &= await Health().writeHealthData( value: 37, type: HealthDataType.BODY_TEMPERATURE, @@ -275,52 +300,52 @@ class _HealthAppState extends State { startTime: now, ); success &= await Health().writeMeal( - mealType: MealType.SNACK, - startTime: earlier, - endTime: now, - caloriesConsumed: 1000, - carbohydrates: 50, - protein: 25, - fatTotal: 50, - name: "Banana", - caffeine: 0.002, - vitaminA: 0.001, - vitaminC: 0.002, - vitaminD: 0.003, - vitaminE: 0.004, - vitaminK: 0.005, - b1Thiamin: 0.006, - b2Riboflavin: 0.007, - b3Niacin: 0.008, - b5PantothenicAcid: 0.009, - b6Pyridoxine: 0.010, - b7Biotin: 0.011, - b9Folate: 0.012, - b12Cobalamin: 0.013, - calcium: 0.015, - copper: 0.016, - iodine: 0.017, - iron: 0.018, - magnesium: 0.019, - manganese: 0.020, - phosphorus: 0.021, - potassium: 0.022, - selenium: 0.023, - sodium: 0.024, - zinc: 0.025, - water: 0.026, - molybdenum: 0.027, - chloride: 0.028, - chromium: 0.029, - cholesterol: 0.030, - fiber: 0.031, - fatMonounsaturated: 0.032, - fatPolyunsaturated: 0.033, - fatUnsaturated: 0.065, - fatTransMonoenoic: 0.65, - fatSaturated: 066, - sugar: 0.067, - ); + mealType: MealType.SNACK, + startTime: earlier, + endTime: now, + caloriesConsumed: 1000, + carbohydrates: 50, + protein: 25, + fatTotal: 50, + name: "Banana", + caffeine: 0.002, + vitaminA: 0.001, + vitaminC: 0.002, + vitaminD: 0.003, + vitaminE: 0.004, + vitaminK: 0.005, + b1Thiamin: 0.006, + b2Riboflavin: 0.007, + b3Niacin: 0.008, + b5PantothenicAcid: 0.009, + b6Pyridoxine: 0.010, + b7Biotin: 0.011, + b9Folate: 0.012, + b12Cobalamin: 0.013, + calcium: 0.015, + copper: 0.016, + iodine: 0.017, + iron: 0.018, + magnesium: 0.019, + manganese: 0.020, + phosphorus: 0.021, + potassium: 0.022, + selenium: 0.023, + sodium: 0.024, + zinc: 0.025, + water: 0.026, + molybdenum: 0.027, + chloride: 0.028, + chromium: 0.029, + cholesterol: 0.030, + fiber: 0.031, + fatMonounsaturated: 0.032, + fatPolyunsaturated: 0.033, + fatUnsaturated: 0.065, + fatTransMonoenoic: 0.65, + fatSaturated: 066, + sugar: 0.067, + recordingMethod: RecordingMethod.manual); // Store an Audiogram - only available on iOS // const frequencies = [125.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0]; @@ -386,7 +411,9 @@ class _HealthAppState extends State { if (stepsPermission) { try { - steps = await Health().getTotalStepsInInterval(midnight, now); + steps = await Health().getTotalStepsInInterval(midnight, now, + includeManualEntry: + !recordingMethodsToFilter.contains(RecordingMethod.manual)); } catch (error) { debugPrint("Exception in getTotalStepsInInterval: $error"); } @@ -408,6 +435,7 @@ class _HealthAppState extends State { setState(() => _state = AppState.PERMISSIONS_REVOKING); bool success = false; + try { await Health().revokePermissions(); success = true; @@ -498,6 +526,8 @@ class _HealthAppState extends State { ], ), Divider(thickness: 3), + if (_state == AppState.DATA_READY) _dataFiltration, + if (_state == AppState.STEPS_READY) _stepsFiltration, Expanded(child: Center(child: _content)) ], ), @@ -506,6 +536,84 @@ class _HealthAppState extends State { ); } + Widget get _dataFiltration => Column( + children: [ + Wrap( + children: [ + for (final method in Platform.isAndroid + ? [ + RecordingMethod.manual, + RecordingMethod.automatic, + RecordingMethod.active, + RecordingMethod.unknown, + ] + : [ + RecordingMethod.automatic, + RecordingMethod.manual, + ]) + SizedBox( + width: 150, + child: CheckboxListTile( + title: Text( + '${method.name[0].toUpperCase()}${method.name.substring(1)} entries'), + value: !recordingMethodsToFilter.contains(method), + onChanged: (value) { + setState(() { + if (value!) { + recordingMethodsToFilter.remove(method); + } else { + recordingMethodsToFilter.add(method); + } + fetchData(); + }); + }, + controlAffinity: ListTileControlAffinity.leading, + contentPadding: EdgeInsets.zero, + dense: true, + ), + ), + // Add other entries here if needed + ], + ), + Divider(thickness: 3), + ], + ); + + Widget get _stepsFiltration => Column( + children: [ + Wrap( + children: [ + for (final method in [ + RecordingMethod.manual, + ]) + SizedBox( + width: 150, + child: CheckboxListTile( + title: Text( + '${method.name[0].toUpperCase()}${method.name.substring(1)} entries'), + value: !recordingMethodsToFilter.contains(method), + onChanged: (value) { + setState(() { + if (value!) { + recordingMethodsToFilter.remove(method); + } else { + recordingMethodsToFilter.add(method); + } + fetchStepData(); + }); + }, + controlAffinity: ListTileControlAffinity.leading, + contentPadding: EdgeInsets.zero, + dense: true, + ), + ), + // Add other entries here if needed + ], + ), + Divider(thickness: 3), + ], + ); + Widget get _permissionsRevoking => Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -538,12 +646,18 @@ class _HealthAppState extends State { Widget get _contentDataReady => ListView.builder( itemCount: _healthDataList.length, itemBuilder: (_, index) { + // filter out manual entires if not wanted + if (recordingMethodsToFilter + .contains(_healthDataList[index].recordingMethod)) { + return Container(); + } + HealthDataPoint p = _healthDataList[index]; if (p.value is AudiogramHealthValue) { return ListTile( title: Text("${p.typeString}: ${p.value}"), trailing: Text('${p.unitString}'), - subtitle: Text('${p.dateFrom} - ${p.dateTo}'), + subtitle: Text('${p.dateFrom} - ${p.dateTo}\n${p.recordingMethod}'), ); } if (p.value is WorkoutHealthValue) { @@ -552,7 +666,7 @@ class _HealthAppState extends State { "${p.typeString}: ${(p.value as WorkoutHealthValue).totalEnergyBurned} ${(p.value as WorkoutHealthValue).totalEnergyBurnedUnit?.name}"), trailing: Text( '${(p.value as WorkoutHealthValue).workoutActivityType.name}'), - subtitle: Text('${p.dateFrom} - ${p.dateTo}'), + subtitle: Text('${p.dateFrom} - ${p.dateTo}\n${p.recordingMethod}'), ); } if (p.value is NutritionHealthValue) { @@ -561,13 +675,13 @@ class _HealthAppState extends State { "${p.typeString} ${(p.value as NutritionHealthValue).mealType}: ${(p.value as NutritionHealthValue).name}"), trailing: Text('${(p.value as NutritionHealthValue).calories} kcal'), - subtitle: Text('${p.dateFrom} - ${p.dateTo}'), + subtitle: Text('${p.dateFrom} - ${p.dateTo}\n${p.recordingMethod}'), ); } return ListTile( title: Text("${p.typeString}: ${p.value}"), trailing: Text('${p.unitString}'), - subtitle: Text('${p.dateFrom} - ${p.dateTo}'), + subtitle: Text('${p.dateFrom} - ${p.dateTo}\n${p.recordingMethod}'), ); }); diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 19d8cc207..654f89991 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -2,6 +2,13 @@ import Flutter import HealthKit import UIKit +enum RecordingMethod: Int { + case unknown = 0 // RECORDING_METHOD_UNKNOWN (not supported on iOS) + case active = 1 // RECORDING_METHOD_ACTIVELY_RECORDED (not supported on iOS) + case automatic = 2 // RECORDING_METHOD_AUTOMATICALLY_RECORDED + case manual = 3 // RECORDING_METHOD_MANUAL_ENTRY +} + public class SwiftHealthPlugin: NSObject, FlutterPlugin { let healthStore = HKHealthStore() @@ -414,25 +421,31 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let type = (arguments["dataTypeKey"] as? String), let unit = (arguments["dataUnitKey"] as? String), let startTime = (arguments["startTime"] as? NSNumber), - let endTime = (arguments["endTime"] as? NSNumber) + let endTime = (arguments["endTime"] as? NSNumber), + let recordingMethod = (arguments["recordingMethod"] as? Int) else { throw PluginError(message: "Invalid Arguments") } let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) + + let isManualEntry = recordingMethod == RecordingMethod.manual.rawValue + let metadata: [String: Any] = [ + HKMetadataKeyWasUserEntered: NSNumber(value: isManualEntry) + ] let sample: HKObject if dataTypeLookUp(key: type).isKind(of: HKCategoryType.self) { sample = HKCategorySample( type: dataTypeLookUp(key: type) as! HKCategoryType, value: Int(value), start: dateFrom, - end: dateTo) + end: dateTo, metadata: metadata) } else { let quantity = HKQuantity(unit: unitDict[unit]!, doubleValue: value) sample = HKQuantitySample( type: dataTypeLookUp(key: type) as! HKQuantityType, quantity: quantity, start: dateFrom, - end: dateTo) + end: dateTo, metadata: metadata) } HKHealthStore().save( @@ -506,21 +519,27 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let systolic = (arguments["systolic"] as? Double), let diastolic = (arguments["diastolic"] as? Double), let startTime = (arguments["startTime"] as? NSNumber), - let endTime = (arguments["endTime"] as? NSNumber) + let endTime = (arguments["endTime"] as? NSNumber), + let recordingMethod = (arguments["recordingMethod"] as? Int) else { throw PluginError(message: "Invalid Arguments") } let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) + + let isManualEntry = recordingMethod == RecordingMethod.manual.rawValue + let metadata = [ + HKMetadataKeyWasUserEntered: NSNumber(value: isManualEntry) + ] let systolic_sample = HKQuantitySample( type: HKSampleType.quantityType(forIdentifier: .bloodPressureSystolic)!, quantity: HKQuantity(unit: HKUnit.millimeterOfMercury(), doubleValue: systolic), - start: dateFrom, end: dateTo) + start: dateFrom, end: dateTo, metadata: metadata) let diastolic_sample = HKQuantitySample( type: HKSampleType.quantityType(forIdentifier: .bloodPressureDiastolic)!, quantity: HKQuantity(unit: HKUnit.millimeterOfMercury(), doubleValue: diastolic), - start: dateFrom, end: dateTo) + start: dateFrom, end: dateTo, metadata: metadata) let bpCorrelationType = HKCorrelationType.correlationType(forIdentifier: .bloodPressure)! let bpCorrelation = Set(arrayLiteral: systolic_sample, diastolic_sample) let blood_pressure_sample = HKCorrelation(type: bpCorrelationType , start: dateFrom, end: dateTo, objects: bpCorrelation) @@ -542,7 +561,8 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let name = (arguments["name"] as? String?), let startTime = (arguments["start_time"] as? NSNumber), let endTime = (arguments["end_time"] as? NSNumber), - let mealType = (arguments["meal_type"] as? String?) + let mealType = (arguments["meal_type"] as? String?), + let recordingMethod = arguments["recordingMethod"] as? Int else { throw PluginError(message: "Invalid Arguments") } @@ -551,12 +571,14 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) let mealTypeString = mealType ?? "UNKNOWN" - var metadata = ["HKFoodMeal": "\(mealTypeString)"] + + let isManualEntry = recordingMethod == RecordingMethod.manual.rawValue - if(name != nil) { + var metadata = ["HKFoodMeal": mealTypeString, HKMetadataKeyWasUserEntered: NSNumber(value: isManualEntry)] as [String : Any] + if (name != nil) { metadata[HKMetadataKeyFoodType] = "\(name!)" } - + var nutrition = Set() for (key, identifier) in NUTRITION_KEYS { let value = arguments[key] as? Double @@ -615,7 +637,8 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { guard let arguments = call.arguments as? NSDictionary, let flow = (arguments["value"] as? Int), let endTime = (arguments["endTime"] as? NSNumber), - let isStartOfCycle = (arguments["isStartOfCycle"] as? NSNumber) + let isStartOfCycle = (arguments["isStartOfCycle"] as? NSNumber), + let recordingMethod = (arguments["recordingMethod"] as? Int) else { throw PluginError(message: "Invalid Arguments - value, startTime, endTime or isStartOfCycle invalid") } @@ -625,11 +648,13 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let dateTime = Date(timeIntervalSince1970: endTime.doubleValue / 1000) + let isManualEntry = recordingMethod == RecordingMethod.manual.rawValue + guard let categoryType = HKSampleType.categoryType(forIdentifier: .menstrualFlow) else { throw PluginError(message: "Invalid Menstrual Flow Type") } - let metadata = [HKMetadataKeyMenstrualCycleStart: isStartOfCycle] + let metadata = [HKMetadataKeyMenstrualCycleStart: isStartOfCycle, HKMetadataKeyWasUserEntered: NSNumber(value: isManualEntry)] as [String : Any] let sample = HKCategorySample( type: categoryType, @@ -743,7 +768,8 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let startTime = (arguments?["startTime"] as? NSNumber) ?? 0 let endTime = (arguments?["endTime"] as? NSNumber) ?? 0 let limit = (arguments?["limit"] as? Int) ?? HKObjectQueryNoLimit - let includeManualEntry = (arguments?["includeManualEntry"] as? Bool) ?? true + let recordingMethodsToFilter = (arguments?["recordingMethodsToFilter"] as? [Int]) ?? [] + let includeManualEntry = !recordingMethodsToFilter.contains(RecordingMethod.manual.rawValue) // Convert dates from milliseconds to Date() let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) @@ -768,7 +794,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "date_to": Int(dateTo.timeIntervalSince1970 * 1000), "source_id": sourceIdForCharacteristic, "source_name": sourceNameForCharacteristic, - "is_manual_entry": true + "recording_method": RecordingMethod.manual.rawValue ] ]) return @@ -781,7 +807,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "date_to": Int(dateTo.timeIntervalSince1970 * 1000), "source_id": sourceIdForCharacteristic, "source_name": sourceNameForCharacteristic, - "is_manual_entry": true + "recording_method": RecordingMethod.manual.rawValue ] ]) return @@ -794,7 +820,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "date_to": Int(dateTo.timeIntervalSince1970 * 1000), "source_id": sourceIdForCharacteristic, "source_name": sourceNameForCharacteristic, - "is_manual_entry": true + "recording_method": RecordingMethod.manual.rawValue ] ]) return @@ -826,7 +852,9 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), "source_id": sample.sourceRevision.source.bundleIdentifier, "source_name": sample.sourceRevision.source.name, - "is_manual_entry": sample.metadata?[HKMetadataKeyWasUserEntered] != nil, + "recording_method": (sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool == true) + ? RecordingMethod.manual.rawValue + : RecordingMethod.automatic.rawValue, "metadata": dataTypeKey == INSULIN_DELIVERY ? sample.metadata : nil ] } @@ -891,7 +919,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), "source_id": sample.sourceRevision.source.bundleIdentifier, "source_name": sample.sourceRevision.source.name, - "is_manual_entry": sample.metadata?[HKMetadataKeyWasUserEntered] != nil, + "recording_method": (sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool == true) ? RecordingMethod.manual.rawValue : RecordingMethod.automatic.rawValue, "metadata": metadata ] } @@ -915,7 +943,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), "source_id": sample.sourceRevision.source.bundleIdentifier, "source_name": sample.sourceRevision.source.name, - "is_manual_entry": sample.metadata?[HKMetadataKeyWasUserEntered] != nil, + "recording_method": (sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool == true) ? RecordingMethod.manual.rawValue : RecordingMethod.automatic.rawValue, "workout_type": self.getWorkoutType(type: sample.workoutActivityType), "total_distance": sample.totalDistance != nil ? Int(sample.totalDistance!.doubleValue(for: HKUnit.meter())) : 0, "total_energy_burned": sample.totalEnergyBurned != nil ? Int(sample.totalEnergyBurned!.doubleValue(for: HKUnit.kilocalorie())) : 0 @@ -969,6 +997,9 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "date_to": Int(sample.endDate.timeIntervalSince1970 * 1000), "source_id": sample.sourceRevision.source.bundleIdentifier, "source_name": sample.sourceRevision.source.name, + "recording_method": (sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool == true) + ? RecordingMethod.manual.rawValue + : RecordingMethod.automatic.rawValue ] for sample in samples { if let quantitySample = sample as? HKQuantitySample { @@ -1047,7 +1078,8 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let startDate = (arguments?["startTime"] as? NSNumber) ?? 0 let endDate = (arguments?["endTime"] as? NSNumber) ?? 0 let intervalInSecond = (arguments?["interval"] as? Int) ?? 1 - let includeManualEntry = (arguments?["includeManualEntry"] as? Bool) ?? true + let recordingMethodsToFilter = (arguments?["recordingMethodsToFilter"] as? [Int]) ?? [] + let includeManualEntry = !recordingMethodsToFilter.contains(RecordingMethod.manual.rawValue) // Set interval in seconds. var interval = DateComponents() @@ -1131,7 +1163,8 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let arguments = call.arguments as? NSDictionary let startTime = (arguments?["startTime"] as? NSNumber) ?? 0 let endTime = (arguments?["endTime"] as? NSNumber) ?? 0 - let includeManualEntry = (arguments?["includeManualEntry"] as? Bool) ?? true + let recordingMethodsToFilter = (arguments?["recordingMethodsToFilter"] as? [Int]) ?? [] + let includeManualEntry = !recordingMethodsToFilter.contains(RecordingMethod.manual.rawValue) // Convert dates from milliseconds to Date() let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) @@ -1743,5 +1776,5 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { default: return "other" } - } + } } diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart index 13f14c5a6..565741982 100644 --- a/packages/health/lib/health.g.dart +++ b/packages/health/lib/health.g.dart @@ -19,7 +19,9 @@ HealthDataPoint _$HealthDataPointFromJson(Map json) => sourceDeviceId: json['source_device_id'] as String, sourceId: json['source_id'] as String, sourceName: json['source_name'] as String, - isManualEntry: json['is_manual_entry'] as bool? ?? false, + recordingMethod: $enumDecodeNullable( + _$RecordingMethodEnumMap, json['recording_method']) ?? + RecordingMethod.unknown, workoutSummary: json['workout_summary'] == null ? null : WorkoutSummary.fromJson( @@ -39,7 +41,7 @@ Map _$HealthDataPointToJson(HealthDataPoint instance) { 'source_device_id': instance.sourceDeviceId, 'source_id': instance.sourceId, 'source_name': instance.sourceName, - 'is_manual_entry': instance.isManualEntry, + 'recording_method': _$RecordingMethodEnumMap[instance.recordingMethod]!, }; void writeNotNull(String key, dynamic value) { @@ -211,6 +213,13 @@ const _$HealthPlatformTypeEnumMap = { HealthPlatformType.googleHealthConnect: 'googleHealthConnect', }; +const _$RecordingMethodEnumMap = { + RecordingMethod.unknown: 'unknown', + RecordingMethod.active: 'active', + RecordingMethod.automatic: 'automatic', + RecordingMethod.manual: 'manual', +}; + HealthValue _$HealthValueFromJson(Map json) => HealthValue()..$type = json['__type'] as String?; diff --git a/packages/health/lib/src/health_data_point.dart b/packages/health/lib/src/health_data_point.dart index ec5a98a9c..adb597061 100644 --- a/packages/health/lib/src/health_data_point.dart +++ b/packages/health/lib/src/health_data_point.dart @@ -44,8 +44,10 @@ class HealthDataPoint { /// The name of the source from which the data point was fetched. String sourceName; - /// The user entered state of the data point. - bool isManualEntry; + /// How the data point was recorded + /// (on Android: https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/metadata/Metadata#summary) + /// on iOS: either user entered or manual https://developer.apple.com/documentation/healthkit/hkmetadatakeywasuserentered) + RecordingMethod recordingMethod; /// The summary of the workout data point, if available. WorkoutSummary? workoutSummary; @@ -64,7 +66,7 @@ class HealthDataPoint { required this.sourceDeviceId, required this.sourceId, required this.sourceName, - this.isManualEntry = false, + this.recordingMethod = RecordingMethod.unknown, this.workoutSummary, this.metadata, }) { @@ -128,7 +130,6 @@ class HealthDataPoint { DateTime.fromMillisecondsSinceEpoch(dataPoint['date_to'] as int); final String sourceId = dataPoint["source_id"] as String; final String sourceName = dataPoint["source_name"] as String; - final bool isManualEntry = dataPoint["is_manual_entry"] as bool? ?? false; final Map? metadata = dataPoint["metadata"] == null ? null : Map.from(dataPoint['metadata'] as Map); @@ -144,6 +145,8 @@ class HealthDataPoint { workoutSummary = WorkoutSummary.fromHealthDataPoint(dataPoint); } + var recordingMethod = dataPoint["recording_method"] as int?; + return HealthDataPoint( uuid: uuid ?? "", value: value, @@ -155,7 +158,7 @@ class HealthDataPoint { sourceDeviceId: Health().deviceId, sourceId: sourceId, sourceName: sourceName, - isManualEntry: isManualEntry, + recordingMethod: RecordingMethod.fromInt(recordingMethod), workoutSummary: workoutSummary, metadata: metadata, ); @@ -173,7 +176,7 @@ class HealthDataPoint { deviceId: $sourceDeviceId, sourceId: $sourceId, sourceName: $sourceName - isManualEntry: $isManualEntry + recordingMethod: $recordingMethod workoutSummary: $workoutSummary metadata: $metadata"""; @@ -190,7 +193,7 @@ class HealthDataPoint { sourceDeviceId == other.sourceDeviceId && sourceId == other.sourceId && sourceName == other.sourceName && - isManualEntry == other.isManualEntry && + recordingMethod == other.recordingMethod && metadata == other.metadata; @override diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 9375e5c81..368dd6399 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -241,17 +241,17 @@ class Health { Future> _computeAndroidBMI( DateTime startTime, DateTime endTime, - bool includeManualEntry, + List recordingMethodsToFilter, ) async { List heights = await _prepareQuery( - startTime, endTime, HealthDataType.HEIGHT, includeManualEntry); + startTime, endTime, HealthDataType.HEIGHT, recordingMethodsToFilter); if (heights.isEmpty) { return []; } List weights = await _prepareQuery( - startTime, endTime, HealthDataType.WEIGHT, includeManualEntry); + startTime, endTime, HealthDataType.WEIGHT, recordingMethodsToFilter); double h = (heights.last.value as NumericHealthValue).numericValue.toDouble(); @@ -275,7 +275,7 @@ class Health { sourceDeviceId: _deviceId!, sourceId: '', sourceName: '', - isManualEntry: !includeManualEntry, + recordingMethod: RecordingMethod.unknown, ); bmiHealthPoints.add(x); @@ -297,6 +297,8 @@ class Health { /// It must be equal to or later than [startTime]. /// Simply set [endTime] equal to [startTime] if the [value] is measured /// only at a specific point in time (default). + /// * [recordingMethod] - the recording method of the data point, automatic by default. + /// (on iOS this must be manual or automatic) /// /// Values for Sleep and Headache are ignored and will be automatically assigned /// the default value. @@ -306,7 +308,14 @@ class Health { required HealthDataType type, required DateTime startTime, DateTime? endTime, + RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + if (Platform.isIOS && + [RecordingMethod.active, RecordingMethod.unknown] + .contains(recordingMethod)) { + throw ArgumentError("recordingMethod must be manual or automatic on iOS"); + } + if (type == HealthDataType.WORKOUT) { throw ArgumentError( "Adding workouts should be done using the writeWorkoutData method."); @@ -356,7 +365,8 @@ class Health { 'dataTypeKey': type.name, 'dataUnitKey': unit.name, 'startTime': startTime.millisecondsSinceEpoch, - 'endTime': endTime.millisecondsSinceEpoch + 'endTime': endTime.millisecondsSinceEpoch, + 'recordingMethod': recordingMethod.toInt(), }; bool? success = await _channel.invokeMethod('writeData', args); return success ?? false; @@ -404,12 +414,20 @@ class Health { /// Must be equal to or later than [startTime]. /// Simply set [endTime] equal to [startTime] if the blood pressure is measured /// only at a specific point in time. If omitted, [endTime] is set to [startTime]. + /// * [recordingMethod] - the recording method of the data point. Future writeBloodPressure({ required int systolic, required int diastolic, required DateTime startTime, DateTime? endTime, + RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + if (Platform.isIOS && + [RecordingMethod.active, RecordingMethod.unknown] + .contains(recordingMethod)) { + throw ArgumentError("recordingMethod must be manual or automatic on iOS"); + } + endTime ??= startTime; if (startTime.isAfter(endTime)) { throw ArgumentError("startTime must be equal or earlier than endTime"); @@ -419,7 +437,8 @@ class Health { 'systolic': systolic, 'diastolic': diastolic, 'startTime': startTime.millisecondsSinceEpoch, - 'endTime': endTime.millisecondsSinceEpoch + 'endTime': endTime.millisecondsSinceEpoch, + 'recordingMethod': recordingMethod.toInt(), }; return await _channel.invokeMethod('writeBloodPressure', args) == true; } @@ -436,11 +455,19 @@ class Health { /// Must be equal to or later than [startTime]. /// Simply set [endTime] equal to [startTime] if the blood oxygen saturation /// is measured only at a specific point in time (default). + /// * [recordingMethod] - the recording method of the data point. Future writeBloodOxygen({ required double saturation, required DateTime startTime, DateTime? endTime, + RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + if (Platform.isIOS && + [RecordingMethod.active, RecordingMethod.unknown] + .contains(recordingMethod)) { + throw ArgumentError("recordingMethod must be manual or automatic on iOS"); + } + endTime ??= startTime; if (startTime.isAfter(endTime)) { throw ArgumentError("startTime must be equal or earlier than endTime"); @@ -452,13 +479,15 @@ class Health { value: saturation, type: HealthDataType.BLOOD_OXYGEN, startTime: startTime, - endTime: endTime); + endTime: endTime, + recordingMethod: recordingMethod); } else if (Platform.isAndroid) { Map args = { 'value': saturation, 'startTime': startTime.millisecondsSinceEpoch, 'endTime': endTime.millisecondsSinceEpoch, 'dataTypeKey': HealthDataType.BLOOD_OXYGEN.name, + 'recordingMethod': recordingMethod.toInt(), }; success = await _channel.invokeMethod('writeBloodOxygen', args); } @@ -517,6 +546,7 @@ class Health { /// * [sugar] - optional sugar information. /// * [water] - optional water information. /// * [zinc] - optional zinc information. + /// * [recordingMethod] - the recording method of the data point. Future writeMeal({ required MealType mealType, required DateTime startTime, @@ -563,7 +593,14 @@ class Health { double? sugar, double? water, double? zinc, + RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + if (Platform.isIOS && + [RecordingMethod.active, RecordingMethod.unknown] + .contains(recordingMethod)) { + throw ArgumentError("recordingMethod must be manual or automatic on iOS"); + } + if (startTime.isAfter(endTime)) { throw ArgumentError("startTime must be equal or earlier than endTime"); } @@ -614,6 +651,7 @@ class Health { 'sugar': sugar, 'water': water, 'zinc': zinc, + 'recordingMethod': recordingMethod.toInt(), }; bool? success = await _channel.invokeMethod('writeMeal', args); return success ?? false; @@ -629,12 +667,20 @@ class Health { /// * [endTime] - the start time when the menstrual flow is measured. /// * [isStartOfCycle] - A bool that indicates whether the sample represents /// the start of a menstrual cycle. + /// * [recordingMethod] - the recording method of the data point. Future writeMenstruationFlow({ required MenstrualFlow flow, required DateTime startTime, required DateTime endTime, required bool isStartOfCycle, + RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + if (Platform.isIOS && + [RecordingMethod.active, RecordingMethod.unknown] + .contains(recordingMethod)) { + throw ArgumentError("recordingMethod must be manual or automatic on iOS"); + } + var value = Platform.isAndroid ? MenstrualFlow.toHealthConnect(flow) : flow.index; @@ -649,6 +695,7 @@ class Health { 'endTime': endTime.millisecondsSinceEpoch, 'isStartOfCycle': isStartOfCycle, 'dataTypeKey': HealthDataType.MENSTRUATION_FLOW.name, + 'recordingMethod': recordingMethod.toInt(), }; return await _channel.invokeMethod('writeMenstruationFlow', args) == true; } @@ -750,17 +797,19 @@ class Health { } /// Fetch a list of health data points based on [types]. + /// You can also specify the [recordingMethodsToFilter] to filter the data points. + /// If not specified, all data points will be included. Future> getHealthDataFromTypes({ required List types, required DateTime startTime, required DateTime endTime, - bool includeManualEntry = true, + List recordingMethodsToFilter = const [], }) async { List dataPoints = []; for (var type in types) { - final result = - await _prepareQuery(startTime, endTime, type, includeManualEntry); + final result = await _prepareQuery( + startTime, endTime, type, recordingMethodsToFilter); dataPoints.addAll(result); } @@ -773,17 +822,19 @@ class Health { } /// Fetch a list of health data points based on [types]. + /// You can also specify the [recordingMethodsToFilter] to filter the data points. + /// If not specified, all data points will be included.Vkk Future> getHealthIntervalDataFromTypes( {required DateTime startDate, required DateTime endDate, required List types, required int interval, - bool includeManualEntry = true}) async { + List recordingMethodsToFilter = const []}) async { List dataPoints = []; for (var type in types) { final result = await _prepareIntervalQuery( - startDate, endDate, type, interval, includeManualEntry); + startDate, endDate, type, interval, recordingMethodsToFilter); dataPoints.addAll(result); } @@ -812,7 +863,7 @@ class Health { DateTime startTime, DateTime endTime, HealthDataType dataType, - bool includeManualEntry, + List recordingMethodsToFilter, ) async { // Ask for device ID only once _deviceId ??= Platform.isAndroid @@ -827,9 +878,10 @@ class Health { // If BodyMassIndex is requested on Android, calculate this manually if (dataType == HealthDataType.BODY_MASS_INDEX && Platform.isAndroid) { - return _computeAndroidBMI(startTime, endTime, includeManualEntry); + return _computeAndroidBMI(startTime, endTime, recordingMethodsToFilter); } - return await _dataQuery(startTime, endTime, dataType, includeManualEntry); + return await _dataQuery( + startTime, endTime, dataType, recordingMethodsToFilter); } /// Prepares an interval query, i.e. checks if the types are available, etc. @@ -838,7 +890,7 @@ class Health { DateTime endDate, HealthDataType dataType, int interval, - bool includeManualEntry) async { + List recordingMethodsToFilter) async { // Ask for device ID only once _deviceId ??= Platform.isAndroid ? (await _deviceInfo.androidInfo).id @@ -851,7 +903,7 @@ class Health { } return await _dataIntervalQuery( - startDate, endDate, dataType, interval, includeManualEntry); + startDate, endDate, dataType, interval, recordingMethodsToFilter); } /// Prepares an aggregate query, i.e. checks if the types are available, etc. @@ -878,14 +930,18 @@ class Health { } /// Fetches data points from Android/iOS native code. - Future> _dataQuery(DateTime startTime, DateTime endTime, - HealthDataType dataType, bool includeManualEntry) async { + Future> _dataQuery( + DateTime startTime, + DateTime endTime, + HealthDataType dataType, + List recordingMethodsToFilter) async { final args = { 'dataTypeKey': dataType.name, 'dataUnitKey': dataTypeToUnit[dataType]!.name, 'startTime': startTime.millisecondsSinceEpoch, 'endTime': endTime.millisecondsSinceEpoch, - 'includeManualEntry': includeManualEntry + 'recordingMethodsToFilter': + recordingMethodsToFilter.map((e) => e.toInt()).toList(), }; final fetchedDataPoints = await _channel.invokeMethod('getData', args); @@ -912,14 +968,15 @@ class Health { DateTime endDate, HealthDataType dataType, int interval, - bool includeManualEntry) async { + List recordingMethodsToFilter) async { final args = { 'dataTypeKey': dataType.name, 'dataUnitKey': dataTypeToUnit[dataType]!.name, 'startTime': startDate.millisecondsSinceEpoch, 'endTime': endDate.millisecondsSinceEpoch, 'interval': interval, - 'includeManualEntry': includeManualEntry + 'recordingMethodsToFilter': + recordingMethodsToFilter.map((e) => e.toInt()).toList(), }; final fetchedDataPoints = @@ -983,7 +1040,9 @@ class Health { final args = { 'startTime': startTime.millisecondsSinceEpoch, 'endTime': endTime.millisecondsSinceEpoch, - 'includeManualEntry': includeManualEntry + 'recordingMethodsToFilter': includeManualEntry + ? [] + : [RecordingMethod.manual.toInt()], }; final stepsCount = await _channel.invokeMethod( 'getTotalStepsInInterval', @@ -1027,6 +1086,7 @@ class Health { /// *ONLY FOR IOS* Default value is METER. /// - [title] The title of the workout. /// *ONLY FOR HEALTH CONNECT* Default value is the [activityType], e.g. "STRENGTH_TRAINING". + /// - [recordingMethod] The recording method of the data point, automatic by default (on iOS this can only be automatic or manual). Future writeWorkoutData({ required HealthWorkoutActivityType activityType, required DateTime start, @@ -1036,7 +1096,14 @@ class Health { int? totalDistance, HealthDataUnit totalDistanceUnit = HealthDataUnit.METER, String? title, + RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + if (Platform.isIOS && + [RecordingMethod.active, RecordingMethod.unknown] + .contains(recordingMethod)) { + throw ArgumentError("recordingMethod must be manual or automatic on iOS"); + } + // Check that value is on the current Platform if (Platform.isIOS && !_isOnIOS(activityType)) { throw HealthException(activityType, @@ -1054,6 +1121,7 @@ class Health { 'totalDistance': totalDistance, 'totalDistanceUnit': totalDistanceUnit.name, 'title': title, + 'recordingMethod': recordingMethod.toInt(), }; return await _channel.invokeMethod('writeWorkoutData', args) == true; } diff --git a/packages/health/lib/src/health_value_types.dart b/packages/health/lib/src/health_value_types.dart index 502328b51..d313104ca 100644 --- a/packages/health/lib/src/health_value_types.dart +++ b/packages/health/lib/src/health_value_types.dart @@ -818,6 +818,46 @@ enum MenstrualFlow { } } +enum RecordingMethod { + unknown, + active, + automatic, + manual; + + /// Create a [RecordingMethod] from an integer. + /// 0: unknown, 1: active, 2: automatic, 3: manual + /// If the integer is not in the range of 0-3, [RecordingMethod.unknown] is returned. + /// This is used to align the recording method with the platform. + static RecordingMethod fromInt(int? recordingMethod) { + switch (recordingMethod) { + case 0: + return RecordingMethod.unknown; + case 1: + return RecordingMethod.active; + case 2: + return RecordingMethod.automatic; + case 3: + return RecordingMethod.manual; + default: + return RecordingMethod.unknown; + } + } + + /// Convert this [RecordingMethod] to an integer. + int toInt() { + switch (this) { + case RecordingMethod.unknown: + return 0; + case RecordingMethod.active: + return 1; + case RecordingMethod.automatic: + return 2; + case RecordingMethod.manual: + return 3; + } + } +} + /// A [HealthValue] object for menstrual flow. /// /// Parameters: From 2fb639dc8fceeabfa3c54e45d531823ba83198d6 Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Fri, 30 Aug 2024 14:27:50 +0200 Subject: [PATCH 27/80] [Health] improve support for sleep stages (#1026) * Remove Google Fit and imports from Android code * Formatting * Remove Google Fit column from readme * Remove support for Google Fit types not supported by Health Connect * Remove more Google Fit workout types * Remove references to Google Fit, remove `useHealthConnectIfAvailable` * Remove `disconect` method channel * Remove `flowRate` from `writeBloodOxygen` as it is not supported in Health Connect * Remove more unsupported workout types * Add missing import * Remove Google Fit as dependency * Add notice in README * Improve logging for HC permission callback * Update some documentation * Android: Fix `requestAuthorization` not returning a result on success * Remove additional workout types that are not supported * Remove another workout type * Add missing unimplemented method * Implement sleep stage changes on iOS side * Implement Android side * Minor fix to README * Add support for SLEEP_AWAKE_IN_BED * Fix for [health 10.2.0] #1010 --- packages/health/README.md | 110 +++++++++--------- .../cachet/plugins/health/HealthPlugin.kt | 61 ++++++++++ packages/health/example/lib/util.dart | 8 +- .../ios/Classes/SwiftHealthPlugin.swift | 26 ++--- packages/health/lib/health.g.dart | 11 +- .../health/lib/src/health_data_point.dart | 4 +- packages/health/lib/src/health_plugin.dart | 8 +- packages/health/lib/src/heath_data_types.dart | 37 +++--- 8 files changed, 158 insertions(+), 107 deletions(-) diff --git a/packages/health/README.md b/packages/health/README.md index 1e47314a3..1e3ab89ff 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -257,7 +257,8 @@ flutter: PlatformException(FlutterHealth, Results are null, Optional(Error Doma ``` ### Filtering by recording method -Google Health Connect and Apple HealthKit both provide ways to distinguish samples collected "automatically" and manually entered data by the user. + +Google Health Connect and Apple HealthKit both provide ways to distinguish samples collected "automatically" and manually entered data by the user. - Android provides an enum with 4 variations: https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/metadata/Metadata#summary - iOS has a boolean value: https://developer.apple.com/documentation/healthkit/hkmetadatakeywasuserentered @@ -275,8 +276,7 @@ List healthData = await Health().getHealthDataFromTypes( **Note that for this to work, the information needs to have been provided when writing the data to Health Connect or Apple Health**. For example, steps added manually through the Apple Health App will set `HKWasUserEntered` to true (corresponding to `RecordingMethod.manual`), however it seems that adding steps manually to Google Fit does not write the data with the `RecordingMethod.manual` in the metadata, instead it shows up as `RecordingMethod.unknown`. This is an open issue, and as such filtering manual entries when querying step count on Android with `getTotalStepsInInterval(includeManualEntries: false)` does not necessarily filter out manual steps. -**NOTE**: On iOS, you can only filter by `RecordingMethod.automatic` and `RecordingMethod`.manual` as it is stored `HKMetadataKeyWasUserEntered` is a boolean value in the metadata. - +**NOTE**: On iOS, you can only filter by `RecordingMethod.automatic` and `RecordingMethod.manual` as it is stored `HKMetadataKeyWasUserEntered` is a boolean value in the metadata. ### Filtering out duplicates @@ -301,57 +301,59 @@ points = Health().removeDuplicates(points); The plugin supports the following [`HealthDataType`](https://pub.dev/documentation/health/latest/health/HealthDataType.html). -| **Data Type** | **Unit** | **Apple Health** | **Google Health Connect** | **Comments** | -| ---------------------------- | ----------------------- | ---------------- | ------------------------- | -------------------------------------- | -| ACTIVE_ENERGY_BURNED | CALORIES | yes | yes | | -| BASAL_ENERGY_BURNED | CALORIES | yes | yes | | -| BLOOD_GLUCOSE | MILLIGRAM_PER_DECILITER | yes | yes | | -| BLOOD_OXYGEN | PERCENTAGE | yes | yes | | -| BLOOD_PRESSURE_DIASTOLIC | MILLIMETER_OF_MERCURY | yes | yes | | -| BLOOD_PRESSURE_SYSTOLIC | MILLIMETER_OF_MERCURY | yes | yes | | -| BODY_FAT_PERCENTAGE | PERCENTAGE | yes | yes | | -| BODY_MASS_INDEX | NO_UNIT | yes | yes | | -| BODY_TEMPERATURE | DEGREE_CELSIUS | yes | yes | | -| BODY_WATER_MASS | KILOGRAMS | | yes | | -| ELECTRODERMAL_ACTIVITY | SIEMENS | yes | | | -| HEART_RATE | BEATS_PER_MINUTE | yes | yes | | -| HEIGHT | METERS | yes | yes | | -| RESTING_HEART_RATE | BEATS_PER_MINUTE | yes | yes | | -| RESPIRATORY_RATE | RESPIRATIONS_PER_MINUTE | yes | yes | | -| PERIPHERAL_PERFUSION_INDEX | PERCENTAGE | yes | | | -| STEPS | COUNT | yes | yes | | -| WAIST_CIRCUMFERENCE | METERS | yes | | | -| WALKING_HEART_RATE | BEATS_PER_MINUTE | yes | | | -| WEIGHT | KILOGRAMS | yes | yes | | -| DISTANCE_WALKING_RUNNING | METERS | yes | | | -| FLIGHTS_CLIMBED | COUNT | yes | yes | | -| DISTANCE_DELTA | METERS | | yes | | -| MINDFULNESS | MINUTES | yes | | | -| SLEEP_IN_BED | MINUTES | yes | | | -| SLEEP_ASLEEP | MINUTES | yes | yes | | -| SLEEP_AWAKE | MINUTES | yes | yes | | -| SLEEP_DEEP | MINUTES | yes | yes | | -| SLEEP_LIGHT | MINUTES | | yes | | -| SLEEP_REM | MINUTES | yes | yes | | -| SLEEP_OUT_OF_BED | MINUTES | | yes | | -| SLEEP_SESSION | MINUTES | | yes | | -| WATER | LITER | yes | yes | | -| EXERCISE_TIME | MINUTES | yes | | | -| WORKOUT | NO_UNIT | yes | yes | See table below | -| HIGH_HEART_RATE_EVENT | NO_UNIT | yes | | Requires Apple Watch to write the data | -| LOW_HEART_RATE_EVENT | NO_UNIT | yes | | Requires Apple Watch to write the data | -| IRREGULAR_HEART_RATE_EVENT | NO_UNIT | yes | | Requires Apple Watch to write the data | -| HEART_RATE_VARIABILITY_RMSSD | MILLISECONDS | | yes | | -| HEART_RATE_VARIABILITY_SDNN | MILLISECONDS | yes | | Requires Apple Watch to write the data | -| HEADACHE_NOT_PRESENT | MINUTES | yes | | | -| HEADACHE_MILD | MINUTES | yes | | | -| HEADACHE_MODERATE | MINUTES | yes | | | -| HEADACHE_SEVERE | MINUTES | yes | | | -| HEADACHE_UNSPECIFIED | MINUTES | yes | | | -| AUDIOGRAM | DECIBEL_HEARING_LEVEL | yes | | | -| ELECTROCARDIOGRAM | VOLT | yes | | Requires Apple Watch to write the data | -| NUTRITION | NO_UNIT | yes | yes | | -| INSULIN_DELIVERY | INTERNATIONAL_UNIT | yes | | | +| **Data Type** | **Unit** | **Apple Health** | **Google Health Connect** | **Comments** | +| ---------------------------- | ----------------------- | ---------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| ACTIVE_ENERGY_BURNED | CALORIES | yes | yes | | +| BASAL_ENERGY_BURNED | CALORIES | yes | yes | | +| BLOOD_GLUCOSE | MILLIGRAM_PER_DECILITER | yes | yes | | +| BLOOD_OXYGEN | PERCENTAGE | yes | yes | | +| BLOOD_PRESSURE_DIASTOLIC | MILLIMETER_OF_MERCURY | yes | yes | | +| BLOOD_PRESSURE_SYSTOLIC | MILLIMETER_OF_MERCURY | yes | yes | | +| BODY_FAT_PERCENTAGE | PERCENTAGE | yes | yes | | +| BODY_MASS_INDEX | NO_UNIT | yes | yes | | +| BODY_TEMPERATURE | DEGREE_CELSIUS | yes | yes | | +| BODY_WATER_MASS | KILOGRAMS | | yes | | +| ELECTRODERMAL_ACTIVITY | SIEMENS | yes | | | +| HEART_RATE | BEATS_PER_MINUTE | yes | yes | | +| HEIGHT | METERS | yes | yes | | +| RESTING_HEART_RATE | BEATS_PER_MINUTE | yes | yes | | +| RESPIRATORY_RATE | RESPIRATIONS_PER_MINUTE | yes | yes | | +| PERIPHERAL_PERFUSION_INDEX | PERCENTAGE | yes | | | +| STEPS | COUNT | yes | yes | | +| WAIST_CIRCUMFERENCE | METERS | yes | | | +| WALKING_HEART_RATE | BEATS_PER_MINUTE | yes | | | +| WEIGHT | KILOGRAMS | yes | yes | | +| DISTANCE_WALKING_RUNNING | METERS | yes | | | +| FLIGHTS_CLIMBED | COUNT | yes | yes | | +| DISTANCE_DELTA | METERS | | yes | | +| MINDFULNESS | MINUTES | yes | | | +| SLEEP_ASLEEP | MINUTES | yes | yes | on iOS, this refers to asleepUnspecified, and on Android this refers to STAGE_TYPE_SLEEPING (asleep but specific stage is unknown) | +| SLEEP_AWAKE | MINUTES | yes | yes | | +| SLEEP_AWAKE_IN_BED | MINUTES | | yes | | +| SLEEP_DEEP | MINUTES | yes | yes | | +| SLEEP_IN_BED | MINUTES | yes | | | +| SLEEP_LIGHT | MINUTES | yes | yes | on iOS, this refers to asleepCore | +| SLEEP_OUT_OF_BED | MINUTES | | yes | | +| SLEEP_REM | MINUTES | yes | yes | | +| SLEEP_UNKNOWN | MINUTES | | yes | | +| SLEEP_SESSION | MINUTES | | yes | | +| WATER | LITER | yes | yes | | +| EXERCISE_TIME | MINUTES | yes | | | +| WORKOUT | NO_UNIT | yes | yes | See table below | +| HIGH_HEART_RATE_EVENT | NO_UNIT | yes | | Requires Apple Watch to write the data | +| LOW_HEART_RATE_EVENT | NO_UNIT | yes | | Requires Apple Watch to write the data | +| IRREGULAR_HEART_RATE_EVENT | NO_UNIT | yes | | Requires Apple Watch to write the data | +| HEART_RATE_VARIABILITY_RMSSD | MILLISECONDS | | yes | | +| HEART_RATE_VARIABILITY_SDNN | MILLISECONDS | yes | | Requires Apple Watch to write the data | +| HEADACHE_NOT_PRESENT | MINUTES | yes | | | +| HEADACHE_MILD | MINUTES | yes | | | +| HEADACHE_MODERATE | MINUTES | yes | | | +| HEADACHE_SEVERE | MINUTES | yes | | | +| HEADACHE_UNSPECIFIED | MINUTES | yes | | | +| AUDIOGRAM | DECIBEL_HEARING_LEVEL | yes | | | +| ELECTROCARDIOGRAM | VOLT | yes | | Requires Apple Watch to write the data | +| NUTRITION | NO_UNIT | yes | yes | | +| INSULIN_DELIVERY | INTERNATIONAL_UNIT | yes | | | ## Workout Types diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 043c49f42..70690126a 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -69,12 +69,14 @@ const val MEAL_UNKNOWN = "UNKNOWN" const val NUTRITION = "NUTRITION" const val SLEEP_ASLEEP = "SLEEP_ASLEEP" const val SLEEP_AWAKE = "SLEEP_AWAKE" +const val SLEEP_AWAKE_IN_BED = "SLEEP_AWAKE_IN_BED" const val SLEEP_DEEP = "SLEEP_DEEP" const val SLEEP_IN_BED = "SLEEP_IN_BED" const val SLEEP_LIGHT = "SLEEP_LIGHT" const val SLEEP_OUT_OF_BED = "SLEEP_OUT_OF_BED" const val SLEEP_REM = "SLEEP_REM" const val SLEEP_SESSION = "SLEEP_SESSION" +const val SLEEP_UNKNOWN = "SLEEP_UNKNOWN" const val SNACK = "SNACK" const val WORKOUT = "WORKOUT" @@ -1995,6 +1997,61 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ), ) + SLEEP_AWAKE_IN_BED -> + SleepSessionRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + startZoneOffset = null, + endZoneOffset = null, + stages = + listOf( + SleepSessionRecord + .Stage( + Instant.ofEpochMilli( + startTime + ), + Instant.ofEpochMilli( + endTime + ), + SleepSessionRecord + .STAGE_TYPE_AWAKE_IN_BED + ) + ), + ) + + SLEEP_UNKNOWN -> + SleepSessionRecord( + startTime = + Instant.ofEpochMilli( + startTime + ), + endTime = + Instant.ofEpochMilli( + endTime + ), + startZoneOffset = null, + endZoneOffset = null, + stages = + listOf( + SleepSessionRecord + .Stage( + Instant.ofEpochMilli( + startTime + ), + Instant.ofEpochMilli( + endTime + ), + SleepSessionRecord + .STAGE_TYPE_UNKNOWN + ) + ), + ) SLEEP_SESSION -> SleepSessionRecord( startTime = @@ -2304,12 +2361,14 @@ class HealthPlugin(private var channel: MethodChannel? = null) : private val mapSleepStageToType = hashMapOf( + 0 to SLEEP_UNKNOWN, 1 to SLEEP_AWAKE, 2 to SLEEP_ASLEEP, 3 to SLEEP_OUT_OF_BED, 4 to SLEEP_LIGHT, 5 to SLEEP_DEEP, 6 to SLEEP_REM, + 7 to SLEEP_AWAKE_IN_BED ) private val mapMealTypeToType = @@ -2351,11 +2410,13 @@ class HealthPlugin(private var channel: MethodChannel? = null) : WATER to HydrationRecord::class, SLEEP_ASLEEP to SleepSessionRecord::class, SLEEP_AWAKE to SleepSessionRecord::class, + SLEEP_AWAKE_IN_BED to SleepSessionRecord::class, SLEEP_LIGHT to SleepSessionRecord::class, SLEEP_DEEP to SleepSessionRecord::class, SLEEP_REM to SleepSessionRecord::class, SLEEP_OUT_OF_BED to SleepSessionRecord::class, SLEEP_SESSION to SleepSessionRecord::class, + SLEEP_UNKNOWN to SleepSessionRecord::class, WORKOUT to ExerciseSessionRecord::class, NUTRITION to NutritionRecord::class, RESTING_HEART_RATE to RestingHeartRateRecord::class, diff --git a/packages/health/example/lib/util.dart b/packages/health/example/lib/util.dart index e135a30f6..623d278ef 100644 --- a/packages/health/example/lib/util.dart +++ b/packages/health/example/lib/util.dart @@ -33,6 +33,7 @@ const List dataTypesIOS = [ HealthDataType.SLEEP_AWAKE, HealthDataType.SLEEP_ASLEEP, HealthDataType.SLEEP_IN_BED, + HealthDataType.SLEEP_LIGHT, HealthDataType.SLEEP_DEEP, HealthDataType.SLEEP_REM, HealthDataType.WATER, @@ -81,11 +82,14 @@ const List dataTypesAndroid = [ HealthDataType.STEPS, HealthDataType.DISTANCE_DELTA, HealthDataType.RESPIRATORY_RATE, - HealthDataType.SLEEP_AWAKE, HealthDataType.SLEEP_ASLEEP, - HealthDataType.SLEEP_LIGHT, + HealthDataType.SLEEP_AWAKE_IN_BED, + HealthDataType.SLEEP_AWAKE, HealthDataType.SLEEP_DEEP, + HealthDataType.SLEEP_LIGHT, + HealthDataType.SLEEP_OUT_OF_BED, HealthDataType.SLEEP_REM, + HealthDataType.SLEEP_UNKNOWN, HealthDataType.SLEEP_SESSION, HealthDataType.WATER, HealthDataType.WORKOUT, diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 654f89991..0afe80d6f 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -140,13 +140,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let DISTANCE_CYCLING = "DISTANCE_CYCLING" let FLIGHTS_CLIMBED = "FLIGHTS_CLIMBED" let MINDFULNESS = "MINDFULNESS" - let SLEEP_IN_BED = "SLEEP_IN_BED" let SLEEP_ASLEEP = "SLEEP_ASLEEP" - let SLEEP_ASLEEP_CORE = "SLEEP_ASLEEP_CORE" - let SLEEP_ASLEEP_DEEP = "SLEEP_ASLEEP_DEEP" - let SLEEP_ASLEEP_REM = "SLEEP_ASLEEP_REM" let SLEEP_AWAKE = "SLEEP_AWAKE" let SLEEP_DEEP = "SLEEP_DEEP" + let SLEEP_IN_BED = "SLEEP_IN_BED" + let SLEEP_LIGHT = "SLEEP_LIGHT" let SLEEP_REM = "SLEEP_REM" let EXERCISE_TIME = "EXERCISE_TIME" @@ -867,19 +865,13 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { if dataTypeKey == self.SLEEP_IN_BED { samplesCategory = samplesCategory.filter { $0.value == 0 } } - if dataTypeKey == self.SLEEP_ASLEEP_CORE { - samplesCategory = samplesCategory.filter { $0.value == 3 } - } - if dataTypeKey == self.SLEEP_ASLEEP_DEEP { - samplesCategory = samplesCategory.filter { $0.value == 4 } - } - if dataTypeKey == self.SLEEP_ASLEEP_REM { - samplesCategory = samplesCategory.filter { $0.value == 5 } + if dataTypeKey == self.SLEEP_ASLEEP { + samplesCategory = samplesCategory.filter { $0.value == 1 } } if dataTypeKey == self.SLEEP_AWAKE { samplesCategory = samplesCategory.filter { $0.value == 2 } } - if dataTypeKey == self.SLEEP_ASLEEP { + if dataTypeKey == self.SLEEP_LIGHT { samplesCategory = samplesCategory.filter { $0.value == 3 } } if dataTypeKey == self.SLEEP_DEEP { @@ -1483,14 +1475,12 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataTypesDict[DISTANCE_CYCLING] = HKSampleType.quantityType(forIdentifier: .distanceCycling)! dataTypesDict[FLIGHTS_CLIMBED] = HKSampleType.quantityType(forIdentifier: .flightsClimbed)! dataTypesDict[MINDFULNESS] = HKSampleType.categoryType(forIdentifier: .mindfulSession)! - dataTypesDict[SLEEP_IN_BED] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! - dataTypesDict[SLEEP_ASLEEP] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! - dataTypesDict[SLEEP_ASLEEP_CORE] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! - dataTypesDict[SLEEP_ASLEEP_DEEP] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! - dataTypesDict[SLEEP_ASLEEP_REM] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! dataTypesDict[SLEEP_AWAKE] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! dataTypesDict[SLEEP_DEEP] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! + dataTypesDict[SLEEP_IN_BED] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! + dataTypesDict[SLEEP_LIGHT] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! dataTypesDict[SLEEP_REM] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! + dataTypesDict[SLEEP_ASLEEP] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! dataTypesDict[MENSTRUATION_FLOW] = HKSampleType.categoryType(forIdentifier: .menstrualFlow)! diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart index 565741982..9be3250db 100644 --- a/packages/health/lib/health.g.dart +++ b/packages/health/lib/health.g.dart @@ -125,17 +125,16 @@ const _$HealthDataTypeEnumMap = { HealthDataType.DISTANCE_DELTA: 'DISTANCE_DELTA', HealthDataType.MINDFULNESS: 'MINDFULNESS', HealthDataType.WATER: 'WATER', - HealthDataType.SLEEP_IN_BED: 'SLEEP_IN_BED', HealthDataType.SLEEP_ASLEEP: 'SLEEP_ASLEEP', - HealthDataType.SLEEP_ASLEEP_CORE: 'SLEEP_ASLEEP_CORE', - HealthDataType.SLEEP_ASLEEP_DEEP: 'SLEEP_ASLEEP_DEEP', - HealthDataType.SLEEP_ASLEEP_REM: 'SLEEP_ASLEEP_REM', + HealthDataType.SLEEP_AWAKE_IN_BED: 'SLEEP_AWAKE_IN_BED', HealthDataType.SLEEP_AWAKE: 'SLEEP_AWAKE', - HealthDataType.SLEEP_LIGHT: 'SLEEP_LIGHT', HealthDataType.SLEEP_DEEP: 'SLEEP_DEEP', - HealthDataType.SLEEP_REM: 'SLEEP_REM', + HealthDataType.SLEEP_IN_BED: 'SLEEP_IN_BED', + HealthDataType.SLEEP_LIGHT: 'SLEEP_LIGHT', HealthDataType.SLEEP_OUT_OF_BED: 'SLEEP_OUT_OF_BED', + HealthDataType.SLEEP_REM: 'SLEEP_REM', HealthDataType.SLEEP_SESSION: 'SLEEP_SESSION', + HealthDataType.SLEEP_UNKNOWN: 'SLEEP_UNKNOWN', HealthDataType.EXERCISE_TIME: 'EXERCISE_TIME', HealthDataType.WORKOUT: 'WORKOUT', HealthDataType.HEADACHE_NOT_PRESENT: 'HEADACHE_NOT_PRESENT', diff --git a/packages/health/lib/src/health_data_point.dart b/packages/health/lib/src/health_data_point.dart index adb597061..2598a0e1b 100644 --- a/packages/health/lib/src/health_data_point.dart +++ b/packages/health/lib/src/health_data_point.dart @@ -78,12 +78,14 @@ class HealthDataPoint { type == HealthDataType.HEADACHE_MILD || type == HealthDataType.HEADACHE_MODERATE || type == HealthDataType.HEADACHE_SEVERE || - type == HealthDataType.SLEEP_IN_BED || type == HealthDataType.SLEEP_ASLEEP || type == HealthDataType.SLEEP_AWAKE || + type == HealthDataType.SLEEP_AWAKE_IN_BED || type == HealthDataType.SLEEP_DEEP || + type == HealthDataType.SLEEP_IN_BED || type == HealthDataType.SLEEP_LIGHT || type == HealthDataType.SLEEP_REM || + type == HealthDataType.SLEEP_UNKNOWN || type == HealthDataType.SLEEP_OUT_OF_BED) { value = _convertMinutes(); } diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 368dd6399..54644341b 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -349,9 +349,7 @@ class Health { type == HealthDataType.SLEEP_IN_BED || type == HealthDataType.SLEEP_DEEP || type == HealthDataType.SLEEP_REM || - type == HealthDataType.SLEEP_ASLEEP_CORE || - type == HealthDataType.SLEEP_ASLEEP_DEEP || - type == HealthDataType.SLEEP_ASLEEP_REM || + type == HealthDataType.SLEEP_LIGHT || type == HealthDataType.HEADACHE_NOT_PRESENT || type == HealthDataType.HEADACHE_MILD || type == HealthDataType.HEADACHE_MODERATE || @@ -1054,13 +1052,11 @@ class Health { /// Assigns numbers to specific [HealthDataType]s. int _alignValue(HealthDataType type) => switch (type) { HealthDataType.SLEEP_IN_BED => 0, + HealthDataType.SLEEP_ASLEEP => 1, HealthDataType.SLEEP_AWAKE => 2, HealthDataType.SLEEP_ASLEEP => 3, HealthDataType.SLEEP_DEEP => 4, HealthDataType.SLEEP_REM => 5, - HealthDataType.SLEEP_ASLEEP_CORE => 3, - HealthDataType.SLEEP_ASLEEP_DEEP => 4, - HealthDataType.SLEEP_ASLEEP_REM => 5, HealthDataType.HEADACHE_UNSPECIFIED => 0, HealthDataType.HEADACHE_NOT_PRESENT => 1, HealthDataType.HEADACHE_MILD => 2, diff --git a/packages/health/lib/src/heath_data_types.dart b/packages/health/lib/src/heath_data_types.dart index c498de3a1..95205e1f8 100644 --- a/packages/health/lib/src/heath_data_types.dart +++ b/packages/health/lib/src/heath_data_types.dart @@ -71,17 +71,16 @@ enum HealthDataType { DISTANCE_DELTA, MINDFULNESS, WATER, - SLEEP_IN_BED, SLEEP_ASLEEP, - SLEEP_ASLEEP_CORE, - SLEEP_ASLEEP_DEEP, - SLEEP_ASLEEP_REM, + SLEEP_AWAKE_IN_BED, SLEEP_AWAKE, - SLEEP_LIGHT, SLEEP_DEEP, - SLEEP_REM, + SLEEP_IN_BED, + SLEEP_LIGHT, SLEEP_OUT_OF_BED, + SLEEP_REM, SLEEP_SESSION, + SLEEP_UNKNOWN, EXERCISE_TIME, WORKOUT, HEADACHE_NOT_PRESENT, @@ -185,14 +184,12 @@ const List dataTypeKeysIOS = [ HealthDataType.DISTANCE_SWIMMING, HealthDataType.DISTANCE_CYCLING, HealthDataType.MINDFULNESS, - HealthDataType.SLEEP_IN_BED, - HealthDataType.SLEEP_AWAKE, HealthDataType.SLEEP_ASLEEP, + HealthDataType.SLEEP_AWAKE, HealthDataType.SLEEP_DEEP, + HealthDataType.SLEEP_IN_BED, + HealthDataType.SLEEP_LIGHT, HealthDataType.SLEEP_REM, - HealthDataType.SLEEP_ASLEEP_CORE, - HealthDataType.SLEEP_ASLEEP_DEEP, - HealthDataType.SLEEP_ASLEEP_REM, HealthDataType.WATER, HealthDataType.EXERCISE_TIME, HealthDataType.WORKOUT, @@ -226,14 +223,15 @@ const List dataTypeKeysAndroid = [ HealthDataType.STEPS, HealthDataType.WEIGHT, HealthDataType.DISTANCE_DELTA, - HealthDataType.SLEEP_AWAKE, HealthDataType.SLEEP_ASLEEP, - HealthDataType.SLEEP_IN_BED, + HealthDataType.SLEEP_AWAKE_IN_BED, + HealthDataType.SLEEP_AWAKE, HealthDataType.SLEEP_DEEP, HealthDataType.SLEEP_LIGHT, - HealthDataType.SLEEP_REM, HealthDataType.SLEEP_OUT_OF_BED, + HealthDataType.SLEEP_REM, HealthDataType.SLEEP_SESSION, + HealthDataType.SLEEP_UNKNOWN, HealthDataType.WATER, HealthDataType.WORKOUT, HealthDataType.RESTING_HEART_RATE, @@ -316,17 +314,16 @@ const Map dataTypeToUnit = { HealthDataType.DISTANCE_DELTA: HealthDataUnit.METER, HealthDataType.WATER: HealthDataUnit.LITER, - HealthDataType.SLEEP_IN_BED: HealthDataUnit.MINUTE, HealthDataType.SLEEP_ASLEEP: HealthDataUnit.MINUTE, - HealthDataType.SLEEP_ASLEEP_CORE: HealthDataUnit.MINUTE, - HealthDataType.SLEEP_ASLEEP_DEEP: HealthDataUnit.MINUTE, - HealthDataType.SLEEP_ASLEEP_REM: HealthDataUnit.MINUTE, HealthDataType.SLEEP_AWAKE: HealthDataUnit.MINUTE, + HealthDataType.SLEEP_AWAKE_IN_BED: HealthDataUnit.MINUTE, HealthDataType.SLEEP_DEEP: HealthDataUnit.MINUTE, - HealthDataType.SLEEP_REM: HealthDataUnit.MINUTE, - HealthDataType.SLEEP_OUT_OF_BED: HealthDataUnit.MINUTE, + HealthDataType.SLEEP_IN_BED: HealthDataUnit.MINUTE, HealthDataType.SLEEP_LIGHT: HealthDataUnit.MINUTE, + HealthDataType.SLEEP_OUT_OF_BED: HealthDataUnit.MINUTE, + HealthDataType.SLEEP_REM: HealthDataUnit.MINUTE, HealthDataType.SLEEP_SESSION: HealthDataUnit.MINUTE, + HealthDataType.SLEEP_UNKNOWN: HealthDataUnit.MINUTE, HealthDataType.MINDFULNESS: HealthDataUnit.MINUTE, HealthDataType.EXERCISE_TIME: HealthDataUnit.MINUTE, From 5eb27081d6b602250bcfa6fad18cf764b4eb1cbc Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Fri, 30 Aug 2024 14:56:02 +0200 Subject: [PATCH 28/80] Add support for atrial fibrillation on iOS (#1031) --- packages/health/README.md | 1 + packages/health/example/lib/util.dart | 1 + packages/health/ios/Classes/SwiftHealthPlugin.swift | 5 +++++ packages/health/lib/health.g.dart | 1 + packages/health/lib/src/health_plugin.dart | 5 +++-- packages/health/lib/src/heath_data_types.dart | 3 +++ 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/health/README.md b/packages/health/README.md index 1e3ab89ff..3d4cca4c6 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -304,6 +304,7 @@ The plugin supports the following [`HealthDataType`](https://pub.dev/documentati | **Data Type** | **Unit** | **Apple Health** | **Google Health Connect** | **Comments** | | ---------------------------- | ----------------------- | ---------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | ACTIVE_ENERGY_BURNED | CALORIES | yes | yes | | +| ATRIAL_FIBRILLATION_BURDEN | PERCENTAGE | yes | | | | BASAL_ENERGY_BURNED | CALORIES | yes | yes | | | BLOOD_GLUCOSE | MILLIGRAM_PER_DECILITER | yes | yes | | | BLOOD_OXYGEN | PERCENTAGE | yes | yes | | diff --git a/packages/health/example/lib/util.dart b/packages/health/example/lib/util.dart index 623d278ef..0a694b0a2 100644 --- a/packages/health/example/lib/util.dart +++ b/packages/health/example/lib/util.dart @@ -52,6 +52,7 @@ const List dataTypesIOS = [ HealthDataType.LOW_HEART_RATE_EVENT, HealthDataType.RESTING_HEART_RATE, HealthDataType.WALKING_HEART_RATE, + HealthDataType.ATRIAL_FIBRILLATION_BURDEN, HealthDataType.NUTRITION, HealthDataType.GENDER, diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 0afe80d6f..d7af83e05 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -27,6 +27,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { // Health Data Type Keys let ACTIVE_ENERGY_BURNED = "ACTIVE_ENERGY_BURNED" + let ATRIAL_FIBRILLATION_BURDEN = "ATRIAL_FIBRILLATION_BURDEN" let AUDIOGRAM = "AUDIOGRAM" let BASAL_ENERGY_BURNED = "BASAL_ENERGY_BURNED" let BLOOD_GLUCOSE = "BLOOD_GLUCOSE" @@ -1607,6 +1608,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { workoutActivityTypeMap["PICKLEBALL"] = HKWorkoutActivityType.pickleball workoutActivityTypeMap["COOLDOWN"] = HKWorkoutActivityType.cooldown } + + if #available(iOS 16.0, *) { + dataTypesDict[ATRIAL_FIBRILLATION_BURDEN] = HKQuantityType.quantityType(forIdentifier: .atrialFibrillationBurden)! + } // Concatenate heart events, headache and health data types (both may be empty) allDataTypes = Set(heartRateEventTypes + healthDataTypes) diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart index 9be3250db..02c49c115 100644 --- a/packages/health/lib/health.g.dart +++ b/packages/health/lib/health.g.dart @@ -57,6 +57,7 @@ Map _$HealthDataPointToJson(HealthDataPoint instance) { const _$HealthDataTypeEnumMap = { HealthDataType.ACTIVE_ENERGY_BURNED: 'ACTIVE_ENERGY_BURNED', + HealthDataType.ATRIAL_FIBRILLATION_BURDEN: 'ATRIAL_FIBRILLATION_BURDEN', HealthDataType.AUDIOGRAM: 'AUDIOGRAM', HealthDataType.BASAL_ENERGY_BURNED: 'BASAL_ENERGY_BURNED', HealthDataType.BLOOD_GLUCOSE: 'BLOOD_GLUCOSE', diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 54644341b..3da6c82b0 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -196,10 +196,11 @@ class Health { 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.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 is not allowed.'); + '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.'); } } } diff --git a/packages/health/lib/src/heath_data_types.dart b/packages/health/lib/src/heath_data_types.dart index 95205e1f8..f50762721 100644 --- a/packages/health/lib/src/heath_data_types.dart +++ b/packages/health/lib/src/heath_data_types.dart @@ -3,6 +3,7 @@ part of '../health.dart'; /// List of all available health data types. enum HealthDataType { ACTIVE_ENERGY_BURNED, + ATRIAL_FIBRILLATION_BURDEN, AUDIOGRAM, BASAL_ENERGY_BURNED, BLOOD_GLUCOSE, @@ -116,6 +117,7 @@ enum HealthDataAccess { /// List of data types available on iOS. const List dataTypeKeysIOS = [ HealthDataType.ACTIVE_ENERGY_BURNED, + HealthDataType.ATRIAL_FIBRILLATION_BURDEN, HealthDataType.AUDIOGRAM, HealthDataType.BASAL_ENERGY_BURNED, HealthDataType.BLOOD_GLUCOSE, @@ -246,6 +248,7 @@ const List dataTypeKeysAndroid = [ /// Maps a [HealthDataType] to a [HealthDataUnit]. const Map dataTypeToUnit = { HealthDataType.ACTIVE_ENERGY_BURNED: HealthDataUnit.KILOCALORIE, + HealthDataType.ATRIAL_FIBRILLATION_BURDEN: HealthDataUnit.PERCENT, HealthDataType.AUDIOGRAM: HealthDataUnit.DECIBEL_HEARING_LEVEL, HealthDataType.BASAL_ENERGY_BURNED: HealthDataUnit.KILOCALORIE, HealthDataType.BLOOD_GLUCOSE: HealthDataUnit.MILLIGRAM_PER_DECILITER, From a57e5dd0c427866d5595d87f9778f77ca1070fcd Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Fri, 30 Aug 2024 15:43:25 +0200 Subject: [PATCH 29/80] [Health] Update changelog and bump version for relase --- packages/health/CHANGELOG.md | 26 ++++++++++++++++++++++++++ packages/health/pubspec.yaml | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 0d464dcb2..5f60e92d5 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,3 +1,29 @@ +## 11.0.0 + +* **BREAKING** Remove Google Fit support in the Android code, as well as Google FIt related dependencies and references throughout the documentation + * Remove `useHealthConnectIfAvailable` from the parameters of `Health().configure()` + * Remove the `disconnect` method which was previously used to disconnect from Google Fit. + * Remove the `flowRate` value from `writeBloodOxygen` as this is not supported by Health Connect. + * Remove support for various `HealthWorkoutActivityType`s which were supported by Google Fit. Some of these do not have suitable alternatives in Google Health Connect (and are not supported on iOS). The list of removed types can be found in PR [#1014](https://github.com/cph-cachet/flutter-plugins/pull/1014) +* **BREAKING** introduce a new `RecordingMethod` enum + * This can be used to filter records by automatic or manual entries when fetching data + * You can also specify the recording method to write in the metadata + * Remove `isManualEntry` from `HealthDataPoint` in favor of `recordingMethod`, of which the value is an enum `RecordingMethod` + * Remove `includeManualEntry` (previously a boolean) from some of the querying methods in favor of `recordingMethodsToFilter`. + * For complete details on relevant changes, see the description of PR [#1023](https://github.com/cph-cachet/flutter-plugins/pull/1023) +* Add support for all sleep stages across iOS and Android + * Clean up relevant documentation + * Remove undocumented sleep stages + * **BREAKING** certain sleep stages were removed/combined into other related stages see PR [#1026](https://github.com/cph-cachet/flutter-plugins/pull/1026) for the complete list of changes and a discussion of the motivation in issue [#985](https://github.com/cph-cachet/flutter-plugins/issues/985) +* Android: Add support for `OTHER` workout type +* Cleaned up workout activity types for consistency across iOS and Android, see PR [#1020](https://github.com/cph-cachet/flutter-plugins/pull/1020) for a complete list of changes +* iOS: add support for menstruation flow, PR [#1008](https://github.com/cph-cachet/flutter-plugins/pull/1008) +* Android: Add support for heart rate variability, PR [#1009](https://github.com/cph-cachet/flutter-plugins/pull/1009) +* iOS: add support for atrial fibrillation burden, PR [#1031](https://github.com/cph-cachet/flutter-plugins/pull/1031) +* Add support for UUIDs in health records for both HealthKit and Health Connect, PR [#1019](https://github.com/cph-cachet/flutter-plugins/pull/1019) +* Fix an issue when querying workouts, the native code could respond with an activity that is not supported in the Health package, causing an error - this will fallback to `HealthWorkoutActivityType.other` - PR [#1016](https://github.com/cph-cachet/flutter-plugins/pull/1016) +* Remove deprecated Android v1 embeddings, PR [#1021](https://github.com/cph-cachet/flutter-plugins/pull/1021) + ## 10.2.0 * Using named parameters in most methods for consistency. diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index dbfe4d751..ffc1be0af 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -1,6 +1,6 @@ name: health description: Wrapper for Apple's HealthKit on iOS and Google's Health Connect on Android. -version: 10.2.0 +version: 11.0.0 homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages/health environment: From 783bfba64357bc31a1272b4b1c6f9a38a3b9923a Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Thu, 19 Sep 2024 21:56:44 +0200 Subject: [PATCH 30/80] Add checks for Health Connect availability on Android when configuring and reading/writing data --- packages/health/lib/src/health_plugin.dart | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 3da6c82b0..3ebb38737 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -27,6 +27,8 @@ class Health { String? _deviceId; final _deviceInfo = DeviceInfoPlugin(); + HealthConnectSdkStatus _healthConnectSdkStatus = + HealthConnectSdkStatus.sdkUnavailable; Health._() { _registerFromJsonFunctions(); @@ -48,11 +50,27 @@ class Health { /// Configure the health plugin. Must be called before using the plugin. Future configure() async { + if (Platform.isAndroid) { + await _checkIfHealthConnectAvailable(); + } + _deviceId = Platform.isAndroid ? (await _deviceInfo.androidInfo).id : (await _deviceInfo.iosInfo).identifierForVendor; } + Future _checkIfHealthConnectAvailable() async { + if (!Platform.isAndroid) return; + + _healthConnectSdkStatus = await getHealthConnectSdkStatus() ?? + HealthConnectSdkStatus.sdkUnavailable; + + if (_healthConnectSdkStatus == HealthConnectSdkStatus.sdkUnavailable || _healthConnectSdkStatus == HealthConnectSdkStatus.sdkUnavailableProviderUpdateRequired) { + throw UnsupportedError( + 'Health Connect is not available on this device, prompt the user to install it using installHealthConnect.'); + } + } + /// Check if a given data type is available on the platform bool isDataTypeAvailable(HealthDataType dataType) => Platform.isAndroid ? dataTypeKeysAndroid.contains(dataType) @@ -86,6 +104,7 @@ class Health { List types, { List? permissions, }) async { + await _checkIfHealthConnectAvailable(); if (permissions != null && permissions.length != types.length) { throw ArgumentError( "The lists of types and permissions must be of same length."); @@ -111,6 +130,7 @@ class Health { /// NOTE: The app must be completely killed and restarted for the changes to take effect. /// Not implemented on iOS as there is no way to programmatically remove access. Future revokePermissions() async { + await _checkIfHealthConnectAvailable(); try { if (Platform.isIOS) { throw UnsupportedError( @@ -183,6 +203,7 @@ class Health { List types, { List? permissions, }) async { + await _checkIfHealthConnectAvailable(); if (permissions != null && permissions.length != types.length) { throw ArgumentError( 'The length of [types] must be same as that of [permissions].'); @@ -311,6 +332,7 @@ class Health { DateTime? endTime, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + await _checkIfHealthConnectAvailable(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -386,6 +408,7 @@ class Health { required DateTime startTime, DateTime? endTime, }) async { + await _checkIfHealthConnectAvailable(); endTime ??= startTime; if (startTime.isAfter(endTime)) { throw ArgumentError("startTime must be equal or earlier than endTime"); @@ -421,6 +444,7 @@ class Health { DateTime? endTime, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + await _checkIfHealthConnectAvailable(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -461,6 +485,7 @@ class Health { DateTime? endTime, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + await _checkIfHealthConnectAvailable(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -594,6 +619,7 @@ class Health { double? zinc, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + await _checkIfHealthConnectAvailable(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -674,6 +700,7 @@ class Health { required bool isStartOfCycle, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + await _checkIfHealthConnectAvailable(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -804,6 +831,7 @@ class Health { required DateTime endTime, List recordingMethodsToFilter = const [], }) async { + await _checkIfHealthConnectAvailable(); List dataPoints = []; for (var type in types) { @@ -829,6 +857,7 @@ class Health { required List types, required int interval, List recordingMethodsToFilter = const []}) async { + await _checkIfHealthConnectAvailable(); List dataPoints = []; for (var type in types) { @@ -848,6 +877,7 @@ class Health { int activitySegmentDuration = 1, bool includeManualEntry = true, }) async { + await _checkIfHealthConnectAvailable(); List dataPoints = []; final result = await _prepareAggregateQuery( @@ -1095,6 +1125,7 @@ class Health { String? title, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { + await _checkIfHealthConnectAvailable(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { From 09d3cdf741c0efb41a50472c303cba9acb69f2e4 Mon Sep 17 00:00:00 2001 From: Aamir Farooq Date: Thu, 19 Sep 2024 22:30:04 +0200 Subject: [PATCH 31/80] Make naming more clear --- packages/health/lib/src/health_plugin.dart | 37 +++++++++++----------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 3ebb38737..30635670d 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -50,22 +50,21 @@ class Health { /// Configure the health plugin. Must be called before using the plugin. Future configure() async { - if (Platform.isAndroid) { - await _checkIfHealthConnectAvailable(); - } - + await _checkIfHealthConnectAvailableOnAndroid(); _deviceId = Platform.isAndroid ? (await _deviceInfo.androidInfo).id : (await _deviceInfo.iosInfo).identifierForVendor; } - Future _checkIfHealthConnectAvailable() async { + Future _checkIfHealthConnectAvailableOnAndroid() async { if (!Platform.isAndroid) return; _healthConnectSdkStatus = await getHealthConnectSdkStatus() ?? HealthConnectSdkStatus.sdkUnavailable; - if (_healthConnectSdkStatus == HealthConnectSdkStatus.sdkUnavailable || _healthConnectSdkStatus == HealthConnectSdkStatus.sdkUnavailableProviderUpdateRequired) { + if (_healthConnectSdkStatus == HealthConnectSdkStatus.sdkUnavailable || + _healthConnectSdkStatus == + HealthConnectSdkStatus.sdkUnavailableProviderUpdateRequired) { throw UnsupportedError( 'Health Connect is not available on this device, prompt the user to install it using installHealthConnect.'); } @@ -104,7 +103,7 @@ class Health { List types, { List? permissions, }) async { - await _checkIfHealthConnectAvailable(); + await _checkIfHealthConnectAvailableOnAndroid(); if (permissions != null && permissions.length != types.length) { throw ArgumentError( "The lists of types and permissions must be of same length."); @@ -130,7 +129,7 @@ class Health { /// NOTE: The app must be completely killed and restarted for the changes to take effect. /// Not implemented on iOS as there is no way to programmatically remove access. Future revokePermissions() async { - await _checkIfHealthConnectAvailable(); + await _checkIfHealthConnectAvailableOnAndroid(); try { if (Platform.isIOS) { throw UnsupportedError( @@ -203,7 +202,7 @@ class Health { List types, { List? permissions, }) async { - await _checkIfHealthConnectAvailable(); + await _checkIfHealthConnectAvailableOnAndroid(); if (permissions != null && permissions.length != types.length) { throw ArgumentError( 'The length of [types] must be same as that of [permissions].'); @@ -332,7 +331,7 @@ class Health { DateTime? endTime, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { - await _checkIfHealthConnectAvailable(); + await _checkIfHealthConnectAvailableOnAndroid(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -408,7 +407,7 @@ class Health { required DateTime startTime, DateTime? endTime, }) async { - await _checkIfHealthConnectAvailable(); + await _checkIfHealthConnectAvailableOnAndroid(); endTime ??= startTime; if (startTime.isAfter(endTime)) { throw ArgumentError("startTime must be equal or earlier than endTime"); @@ -444,7 +443,7 @@ class Health { DateTime? endTime, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { - await _checkIfHealthConnectAvailable(); + await _checkIfHealthConnectAvailableOnAndroid(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -485,7 +484,7 @@ class Health { DateTime? endTime, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { - await _checkIfHealthConnectAvailable(); + await _checkIfHealthConnectAvailableOnAndroid(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -619,7 +618,7 @@ class Health { double? zinc, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { - await _checkIfHealthConnectAvailable(); + await _checkIfHealthConnectAvailableOnAndroid(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -700,7 +699,7 @@ class Health { required bool isStartOfCycle, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { - await _checkIfHealthConnectAvailable(); + await _checkIfHealthConnectAvailableOnAndroid(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { @@ -831,7 +830,7 @@ class Health { required DateTime endTime, List recordingMethodsToFilter = const [], }) async { - await _checkIfHealthConnectAvailable(); + await _checkIfHealthConnectAvailableOnAndroid(); List dataPoints = []; for (var type in types) { @@ -857,7 +856,7 @@ class Health { required List types, required int interval, List recordingMethodsToFilter = const []}) async { - await _checkIfHealthConnectAvailable(); + await _checkIfHealthConnectAvailableOnAndroid(); List dataPoints = []; for (var type in types) { @@ -877,7 +876,7 @@ class Health { int activitySegmentDuration = 1, bool includeManualEntry = true, }) async { - await _checkIfHealthConnectAvailable(); + await _checkIfHealthConnectAvailableOnAndroid(); List dataPoints = []; final result = await _prepareAggregateQuery( @@ -1125,7 +1124,7 @@ class Health { String? title, RecordingMethod recordingMethod = RecordingMethod.automatic, }) async { - await _checkIfHealthConnectAvailable(); + await _checkIfHealthConnectAvailableOnAndroid(); if (Platform.isIOS && [RecordingMethod.active, RecordingMethod.unknown] .contains(recordingMethod)) { From ee231bd487f61fde49fd6916a93e5ab3b291fa8b Mon Sep 17 00:00:00 2001 From: bardram Date: Fri, 20 Sep 2024 15:27:40 +0200 Subject: [PATCH 32/80] More gracefull error handling (less trowing) and update to demo app and README --- packages/health/CHANGELOG.md | 4 +- packages/health/README.md | 8 +- packages/health/example/lib/main.dart | 181 +++++++++++---------- packages/health/lib/src/health_plugin.dart | 98 ++++++----- 4 files changed, 159 insertions(+), 132 deletions(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 5f60e92d5..7ed8cf6d2 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -12,7 +12,7 @@ * Remove `includeManualEntry` (previously a boolean) from some of the querying methods in favor of `recordingMethodsToFilter`. * For complete details on relevant changes, see the description of PR [#1023](https://github.com/cph-cachet/flutter-plugins/pull/1023) * Add support for all sleep stages across iOS and Android - * Clean up relevant documentation + * Clean up relevant documentation * Remove undocumented sleep stages * **BREAKING** certain sleep stages were removed/combined into other related stages see PR [#1026](https://github.com/cph-cachet/flutter-plugins/pull/1026) for the complete list of changes and a discussion of the motivation in issue [#985](https://github.com/cph-cachet/flutter-plugins/issues/985) * Android: Add support for `OTHER` workout type @@ -20,7 +20,7 @@ * iOS: add support for menstruation flow, PR [#1008](https://github.com/cph-cachet/flutter-plugins/pull/1008) * Android: Add support for heart rate variability, PR [#1009](https://github.com/cph-cachet/flutter-plugins/pull/1009) * iOS: add support for atrial fibrillation burden, PR [#1031](https://github.com/cph-cachet/flutter-plugins/pull/1031) -* Add support for UUIDs in health records for both HealthKit and Health Connect, PR [#1019](https://github.com/cph-cachet/flutter-plugins/pull/1019) +* Add support for UUIDs in health records for both HealthKit and Health Connect, PR [#1019](https://github.com/cph-cachet/flutter-plugins/pull/1019) * Fix an issue when querying workouts, the native code could respond with an activity that is not supported in the Health package, causing an error - this will fallback to `HealthWorkoutActivityType.other` - PR [#1016](https://github.com/cph-cachet/flutter-plugins/pull/1016) * Remove deprecated Android v1 embeddings, PR [#1021](https://github.com/cph-cachet/flutter-plugins/pull/1021) diff --git a/packages/health/README.md b/packages/health/README.md index 3d4cca4c6..76c3396b7 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -1,6 +1,6 @@ # Health -Enables reading and writing health data from/to Apple Health and Health Connect. +Enables reading and writing health data from/to [Apple Health](https://www.apple.com/health/) and [Google Health Connect](https://health.google/health-connect-android/). > **NOTE:** Google has deprecated the Google Fit API. According to the [documentation](https://developers.google.com/fit/android), as of **May 1st 2024** developers cannot sign up for using the API. As such, this package has removed support for Google Fit as of version 11.0.0 and users are urged to upgrade as soon as possible. @@ -17,7 +17,7 @@ The plugin supports: - cleaning up duplicate data points via the `removeDuplicates` method. - removing data of a given type in a selected period of time using the `delete` method. -Note that for Android, the target phone **needs** to have [Health Connect](https://health.google/health-connect-android/) (which is currently in beta) installed and have access to the internet, otherwise this plugin will not work. +Note that for Android, the target phone **needs** to have the [Health Connect](https://play.google.com/store/apps/details?id=com.google.android.apps.healthdata&hl=en) app installed (which is currently in beta) and have access to the internet. See the tables below for supported health and workout data types. @@ -260,8 +260,8 @@ flutter: PlatformException(FlutterHealth, Results are null, Optional(Error Doma Google Health Connect and Apple HealthKit both provide ways to distinguish samples collected "automatically" and manually entered data by the user. -- Android provides an enum with 4 variations: https://developer.android.com/reference/kotlin/androidx/health/connect/client/records/metadata/Metadata#summary -- iOS has a boolean value: https://developer.apple.com/documentation/healthkit/hkmetadatakeywasuserentered +- Android provides an enum with 4 variations: +- iOS has a boolean value: As such, when fetching data you have the option to filter the fetched data by recording method as such: diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index 038b4cf67..db1069ddf 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -80,17 +80,18 @@ class _HealthAppState extends State { : HealthDataAccess.READ_WRITE) .toList(); + @override void initState() { - // configure the health plugin before use. + // configure the health plugin before use and check the Health Connect status Health().configure(); + Health().getHealthConnectSdkStatus(); super.initState(); } /// Install Google Health Connect on this phone. - Future installHealthConnect() async { - await Health().installHealthConnect(); - } + Future installHealthConnect() async => + await Health().installHealthConnect(); /// Authorize, i.e. get permissions to access relevant health data. Future authorize() async { @@ -132,7 +133,8 @@ class _HealthAppState extends State { final status = await Health().getHealthConnectSdkStatus(); setState(() { - _contentHealthConnectStatus = Text('Health Connect Status: $status'); + _contentHealthConnectStatus = + Text('Health Connect Status: ${status?.name.toUpperCase()}'); _state = AppState.HEALTH_CONNECT_STATUS; }); } @@ -143,7 +145,7 @@ class _HealthAppState extends State { // get data within the last 24 hours final now = DateTime.now(); - final yesterday = now.subtract(Duration(hours: 24)); + final yesterday = now.subtract(const Duration(hours: 24)); // Clear old data points _healthDataList.clear(); @@ -186,7 +188,7 @@ class _HealthAppState extends State { /// following data types. Future addData() async { final now = DateTime.now(); - final earlier = now.subtract(Duration(minutes: 20)); + final earlier = now.subtract(const Duration(minutes: 20)); // Add data for supported types // NOTE: These are only the ones supported on Androids new API Health Connect. @@ -289,7 +291,7 @@ class _HealthAppState extends State { success &= await Health().writeWorkoutData( activityType: HealthWorkoutActivityType.AMERICAN_FOOTBALL, title: "Random workout name that shows up in Health Connect", - start: now.subtract(Duration(minutes: 15)), + start: now.subtract(const Duration(minutes: 15)), end: now, totalDistance: 2430, totalEnergyBurned: 400, @@ -378,7 +380,7 @@ class _HealthAppState extends State { /// Delete some random health data. Future deleteData() async { final now = DateTime.now(); - final earlier = now.subtract(Duration(hours: 24)); + final earlier = now.subtract(const Duration(hours: 24)); bool success = true; for (HealthDataType type in types) { @@ -459,78 +461,82 @@ class _HealthAppState extends State { appBar: AppBar( title: const Text('Health Example'), ), - body: Container( - child: Column( - children: [ - Wrap( - spacing: 10, - children: [ + body: Column( + children: [ + Wrap( + spacing: 10, + children: [ + if (Platform.isAndroid) TextButton( - onPressed: authorize, - child: Text("Authenticate", - style: TextStyle(color: Colors.white)), - style: ButtonStyle( - backgroundColor: - MaterialStatePropertyAll(Colors.blue))), - if (Platform.isAndroid) + onPressed: getHealthConnectSdkStatus, + style: const ButtonStyle( + backgroundColor: WidgetStatePropertyAll(Colors.blue)), + child: const Text("Check Health Connect Status", + style: TextStyle(color: Colors.white))), + if (Platform.isAndroid && + Health().healthConnectSdkStatus != + HealthConnectSdkStatus.sdkAvailable) + TextButton( + onPressed: installHealthConnect, + style: const ButtonStyle( + backgroundColor: WidgetStatePropertyAll(Colors.blue)), + child: const Text("Install Health Connect", + style: TextStyle(color: Colors.white))), + if (Platform.isIOS || + Platform.isAndroid && + Health().healthConnectSdkStatus == + HealthConnectSdkStatus.sdkAvailable) + Wrap(spacing: 10, children: [ TextButton( - onPressed: getHealthConnectSdkStatus, - child: Text("Check Health Connect Status", - style: TextStyle(color: Colors.white)), - style: ButtonStyle( + onPressed: authorize, + style: const ButtonStyle( backgroundColor: - MaterialStatePropertyAll(Colors.blue))), - TextButton( - onPressed: fetchData, - child: Text("Fetch Data", - style: TextStyle(color: Colors.white)), - style: ButtonStyle( - backgroundColor: - MaterialStatePropertyAll(Colors.blue))), - TextButton( - onPressed: addData, - child: Text("Add Data", - style: TextStyle(color: Colors.white)), - style: ButtonStyle( - backgroundColor: - MaterialStatePropertyAll(Colors.blue))), - TextButton( - onPressed: deleteData, - child: Text("Delete Data", - style: TextStyle(color: Colors.white)), - style: ButtonStyle( - backgroundColor: - MaterialStatePropertyAll(Colors.blue))), - TextButton( - onPressed: fetchStepData, - child: Text("Fetch Step Data", - style: TextStyle(color: Colors.white)), - style: ButtonStyle( - backgroundColor: - MaterialStatePropertyAll(Colors.blue))), - TextButton( - onPressed: revokeAccess, - child: Text("Revoke Access", - style: TextStyle(color: Colors.white)), - style: ButtonStyle( - backgroundColor: - MaterialStatePropertyAll(Colors.blue))), - if (Platform.isAndroid) + WidgetStatePropertyAll(Colors.blue)), + child: const Text("Authenticate", + style: TextStyle(color: Colors.white))), TextButton( - onPressed: installHealthConnect, - child: Text("Install Health Connect", - style: TextStyle(color: Colors.white)), - style: ButtonStyle( + onPressed: fetchData, + style: const ButtonStyle( backgroundColor: - MaterialStatePropertyAll(Colors.blue))), - ], - ), - Divider(thickness: 3), - if (_state == AppState.DATA_READY) _dataFiltration, - if (_state == AppState.STEPS_READY) _stepsFiltration, - Expanded(child: Center(child: _content)) - ], - ), + WidgetStatePropertyAll(Colors.blue)), + child: const Text("Fetch Data", + style: TextStyle(color: Colors.white))), + TextButton( + onPressed: addData, + style: const ButtonStyle( + backgroundColor: + WidgetStatePropertyAll(Colors.blue)), + child: const Text("Add Data", + style: TextStyle(color: Colors.white))), + TextButton( + onPressed: deleteData, + style: const ButtonStyle( + backgroundColor: + WidgetStatePropertyAll(Colors.blue)), + child: const Text("Delete Data", + style: TextStyle(color: Colors.white))), + TextButton( + onPressed: fetchStepData, + style: const ButtonStyle( + backgroundColor: + WidgetStatePropertyAll(Colors.blue)), + child: const Text("Fetch Step Data", + style: TextStyle(color: Colors.white))), + TextButton( + onPressed: revokeAccess, + style: const ButtonStyle( + backgroundColor: + WidgetStatePropertyAll(Colors.blue)), + child: const Text("Revoke Access", + style: TextStyle(color: Colors.white))), + ]), + ], + ), + const Divider(thickness: 3), + if (_state == AppState.DATA_READY) _dataFiltration, + if (_state == AppState.STEPS_READY) _stepsFiltration, + Expanded(child: Center(child: _content)) + ], ), ), ); @@ -575,7 +581,7 @@ class _HealthAppState extends State { // Add other entries here if needed ], ), - Divider(thickness: 3), + const Divider(thickness: 3), ], ); @@ -610,7 +616,7 @@ class _HealthAppState extends State { // Add other entries here if needed ], ), - Divider(thickness: 3), + const Divider(thickness: 3), ], ); @@ -618,11 +624,11 @@ class _HealthAppState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Container( - padding: EdgeInsets.all(20), - child: CircularProgressIndicator( + padding: const EdgeInsets.all(20), + child: const CircularProgressIndicator( strokeWidth: 10, )), - Text('Revoking permissions...') + const Text('Revoking permissions...') ], ); @@ -635,11 +641,11 @@ class _HealthAppState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Container( - padding: EdgeInsets.all(20), - child: CircularProgressIndicator( + padding: const EdgeInsets.all(20), + child: const CircularProgressIndicator( strokeWidth: 10, )), - Text('Fetching data...') + const Text('Fetching data...') ], ); @@ -687,23 +693,24 @@ class _HealthAppState extends State { Widget _contentNoData = const Text('No Data to show'); - Widget _contentNotFetched = const Column(children: [ + Widget _contentNotFetched = + const Column(mainAxisAlignment: MainAxisAlignment.center, children: [ const Text("Press 'Auth' to get permissions to access health data."), const Text("Press 'Fetch Dat' to get health data."), const Text("Press 'Add Data' to add some random health data."), const Text("Press 'Delete Data' to remove some random health data."), - ], mainAxisAlignment: MainAxisAlignment.center); + ]); Widget _authorized = const Text('Authorization granted!'); Widget _authorizationNotGranted = const Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('Authorization not given.'), const Text( 'For Google Health Connect please check if you have added the right permissions and services to the manifest file.'), const Text('For Apple Health check your permissions in Apple Health.'), ], - mainAxisAlignment: MainAxisAlignment.center, ); Widget _contentHealthConnectStatus = const Text( diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 30635670d..8868a519b 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -1,8 +1,8 @@ part of '../health.dart'; -/// Main class for the Plugin. This class works as a singleton and should be accessed -/// via `Health()` factory method. The plugin must be configured using the [configure] method -/// before used. +/// Main class for the Plugin. This class works as a singleton and should be +/// accessed via `Health()` factory method. The plugin must be configured using +/// the [configure] method before used. /// /// Overall, the plugin supports: /// @@ -21,6 +21,17 @@ part of '../health.dart'; /// * Writing different types of specialized health data like the [writeWorkoutData], /// [writeBloodPressure], [writeBloodOxygen], [writeAudiogram], [writeMeal], /// [writeMenstruationFlow], [writeInsulinDelivery] methods. +/// +/// On **Android**, this plugin relies on the Google Health Connect (GHC) SDK. +/// Since Health Connect is not installed on SDK level < 34, the plugin has a +/// set of specialized methods to handle GHC: +/// +/// * [getHealthConnectSdkStatus] to check the status of GHC +/// * [isHealthConnectAvailable] to check if GHC is installed on this phone +/// * [installHealthConnect] to direct the user to the app store to install GHC +/// +/// **Note** that you should check the availability of GHC before using any setter +/// or getter methods. Otherwise, the plugin will throw an exception. class Health { static const MethodChannel _channel = MethodChannel('flutter_health'); static final _instance = Health._(); @@ -34,9 +45,12 @@ class Health { _registerFromJsonFunctions(); } - /// Get the singleton [Health] instance. + /// The singleton [Health] instance. factory Health() => _instance; + /// The latest status on availability of Health Connect SDK on this phone. + HealthConnectSdkStatus get healthConnectSdkStatus => _healthConnectSdkStatus; + /// The type of platform of this device. HealthPlatformType get platformType => Platform.isIOS ? HealthPlatformType.appleHealth @@ -50,26 +64,11 @@ class Health { /// Configure the health plugin. Must be called before using the plugin. Future configure() async { - await _checkIfHealthConnectAvailableOnAndroid(); _deviceId = Platform.isAndroid ? (await _deviceInfo.androidInfo).id : (await _deviceInfo.iosInfo).identifierForVendor; } - Future _checkIfHealthConnectAvailableOnAndroid() async { - if (!Platform.isAndroid) return; - - _healthConnectSdkStatus = await getHealthConnectSdkStatus() ?? - HealthConnectSdkStatus.sdkUnavailable; - - if (_healthConnectSdkStatus == HealthConnectSdkStatus.sdkUnavailable || - _healthConnectSdkStatus == - HealthConnectSdkStatus.sdkUnavailableProviderUpdateRequired) { - throw UnsupportedError( - 'Health Connect is not available on this device, prompt the user to install it using installHealthConnect.'); - } - } - /// Check if a given data type is available on the platform bool isDataTypeAvailable(HealthDataType dataType) => Platform.isAndroid ? dataTypeKeysAndroid.contains(dataType) @@ -128,56 +127,77 @@ class Health { /// /// NOTE: The app must be completely killed and restarted for the changes to take effect. /// Not implemented on iOS as there is no way to programmatically remove access. + /// + /// Android only. On iOS this does nothing. Future revokePermissions() async { + if (Platform.isIOS) return; + await _checkIfHealthConnectAvailableOnAndroid(); try { - if (Platform.isIOS) { - throw UnsupportedError( - 'Revoke permissions is not supported on iOS. Please revoke permissions manually in the settings.'); - } await _channel.invokeMethod('revokePermissions'); - return; } catch (e) { debugPrint('$runtimeType - Exception in revokePermissions(): $e'); } } - /// Returns the current status of Health Connect availability. + /// Checks the current status of Health Connect availability. /// /// See this for more info: /// https://developer.android.com/reference/kotlin/androidx/health/connect/client/HealthConnectClient#getSdkStatus(android.content.Context,kotlin.String) /// - /// Android only. + /// Android only. Returns null on iOS or if an error occurs. Future getHealthConnectSdkStatus() async { + if (Platform.isIOS) return null; + try { - if (Platform.isIOS) { - throw UnsupportedError('Health Connect is not available on iOS.'); - } - final int status = - (await _channel.invokeMethod('getHealthConnectSdkStatus'))!; - return HealthConnectSdkStatus.fromNativeValue(status); + final status = + await _channel.invokeMethod('getHealthConnectSdkStatus'); + _healthConnectSdkStatus = status != null + ? HealthConnectSdkStatus.fromNativeValue(status) + : HealthConnectSdkStatus.sdkUnavailable; + + return _healthConnectSdkStatus; } catch (e) { debugPrint('$runtimeType - Exception in getHealthConnectSdkStatus(): $e'); return null; } } - /// Prompt the user to install the Health Connect app via the installed store - /// (most likely Play Store). + /// Is Google Health Connect available on this phone? /// - /// Android only. + /// Android only. Returns always true on iOS. + Future isHealthConnectAvailable() async => !Platform.isAndroid + ? true + : (await getHealthConnectSdkStatus() == + HealthConnectSdkStatus.sdkAvailable); + + /// Prompt the user to install the Google Health Connect app via the + /// installed store (most likely Play Store). + /// + /// Android only. On iOS this does nothing. Future installHealthConnect() async { + if (Platform.isIOS) return; + try { - if (!Platform.isAndroid) { - throw UnsupportedError( - 'installHealthConnect is only available on Android'); - } await _channel.invokeMethod('installHealthConnect'); } catch (e) { debugPrint('$runtimeType - Exception in installHealthConnect(): $e'); } } + /// Checks if Google Health Connect is available and throws an [UnsupportedError] + /// if not. + /// Internal methods used to check availability before any getter or setter methods. + Future _checkIfHealthConnectAvailableOnAndroid() async { + if (!Platform.isAndroid) return; + + if (!(await isHealthConnectAvailable())) { + throw UnsupportedError( + "Google Health Connect is not available on this Android device. " + "You may prompt the user to install it using the 'installHealthConnect' method"); + } + } + /// Requests permissions to access health data [types]. /// /// Returns true if successful, false otherwise. From f0e8bf9ef72af6e4cd7e918fad35a0df57fd5b58 Mon Sep 17 00:00:00 2001 From: bardram Date: Mon, 23 Sep 2024 16:45:28 +0200 Subject: [PATCH 33/80] Type-safe JSON deserialization using carp_serializable v. 2.0 --- packages/health/CHANGELOG.md | 5 + packages/health/LICENSE | 16 +- packages/health/README.md | 5 + packages/health/example/pubspec.yaml | 2 +- packages/health/lib/health.g.dart | 200 +++++++++--------- .../health/lib/src/health_data_point.dart | 2 +- .../health/lib/src/health_value_types.dart | 36 ++-- packages/health/lib/src/workout_summary.dart | 2 +- packages/health/pubspec.yaml | 2 +- 9 files changed, 136 insertions(+), 134 deletions(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 7ed8cf6d2..a85a38e57 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,3 +1,8 @@ +## 11.1.0 + +* Fix of [#1043](https://github.com/cph-cachet/flutter-plugins/issues/1043) +* Type-safe JSON deserialization using carp_serializable v. 2.0 + ## 11.0.0 * **BREAKING** Remove Google Fit support in the Android code, as well as Google FIt related dependencies and references throughout the documentation diff --git a/packages/health/LICENSE b/packages/health/LICENSE index 7dfb95c95..0edc55d82 100644 --- a/packages/health/LICENSE +++ b/packages/health/LICENSE @@ -1,17 +1,9 @@ MIT License. -Copyright 2019 Copenhagen Center for Health Technology (CACHET) at the Technical University of Denmark (DTU). +Copyright 2020 the Technical University of Denmark (DTU). -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -documentation files (the ”Software”), to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ”Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED ”AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. \ No newline at end of file +THE SOFTWARE IS PROVIDED ”AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/health/README.md b/packages/health/README.md index 76c3396b7..68ec291bc 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -455,3 +455,8 @@ The plugin supports the following [`HealthWorkoutActivityType`](https://pub.dev/ | WRESTLING | yes | | | | YOGA | yes | yes | | | OTHER | yes | yes | | + +## License + +This software is copyright (c) the [Technical University of Denmark (DTU)](https://www.dtu.dk) and is part of the [Copenhagen Research Platform](https://carp.cachet.dk/). +This software is available 'as-is' under a [MIT license](LICENSE). diff --git a/packages/health/example/pubspec.yaml b/packages/health/example/pubspec.yaml index f23767d27..d472b3f03 100644 --- a/packages/health/example/pubspec.yaml +++ b/packages/health/example/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: sdk: flutter cupertino_icons: ^1.0.2 permission_handler: ^10.2.0 - carp_serializable: ^1.1.0 # polymorphic json serialization + carp_serializable: ^2.0.0 # polymorphic json serialization health: path: ../ diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart index 02c49c115..fa4423446 100644 --- a/packages/health/lib/health.g.dart +++ b/packages/health/lib/health.g.dart @@ -12,36 +12,36 @@ HealthDataPoint _$HealthDataPointFromJson(Map json) => value: HealthValue.fromJson(json['value'] as Map), type: $enumDecode(_$HealthDataTypeEnumMap, json['type']), unit: $enumDecode(_$HealthDataUnitEnumMap, json['unit']), - dateFrom: DateTime.parse(json['date_from'] as String), - dateTo: DateTime.parse(json['date_to'] as String), + dateFrom: DateTime.parse(json['dateFrom'] as String), + dateTo: DateTime.parse(json['dateTo'] as String), sourcePlatform: - $enumDecode(_$HealthPlatformTypeEnumMap, json['source_platform']), - sourceDeviceId: json['source_device_id'] as String, - sourceId: json['source_id'] as String, - sourceName: json['source_name'] as String, + $enumDecode(_$HealthPlatformTypeEnumMap, json['sourcePlatform']), + sourceDeviceId: json['sourceDeviceId'] as String, + sourceId: json['sourceId'] as String, + sourceName: json['sourceName'] as String, recordingMethod: $enumDecodeNullable( - _$RecordingMethodEnumMap, json['recording_method']) ?? + _$RecordingMethodEnumMap, json['recordingMethod']) ?? RecordingMethod.unknown, - workoutSummary: json['workout_summary'] == null + workoutSummary: json['workoutSummary'] == null ? null : WorkoutSummary.fromJson( - json['workout_summary'] as Map), + json['workoutSummary'] as Map), metadata: json['metadata'] as Map?, ); Map _$HealthDataPointToJson(HealthDataPoint instance) { final val = { 'uuid': instance.uuid, - 'value': instance.value, + 'value': instance.value.toJson(), 'type': _$HealthDataTypeEnumMap[instance.type]!, 'unit': _$HealthDataUnitEnumMap[instance.unit]!, - 'date_from': instance.dateFrom.toIso8601String(), - 'date_to': instance.dateTo.toIso8601String(), - 'source_platform': _$HealthPlatformTypeEnumMap[instance.sourcePlatform]!, - 'source_device_id': instance.sourceDeviceId, - 'source_id': instance.sourceId, - 'source_name': instance.sourceName, - 'recording_method': _$RecordingMethodEnumMap[instance.recordingMethod]!, + 'dateFrom': instance.dateFrom.toIso8601String(), + 'dateTo': instance.dateTo.toIso8601String(), + 'sourcePlatform': _$HealthPlatformTypeEnumMap[instance.sourcePlatform]!, + 'sourceDeviceId': instance.sourceDeviceId, + 'sourceId': instance.sourceId, + 'sourceName': instance.sourceName, + 'recordingMethod': _$RecordingMethodEnumMap[instance.recordingMethod]!, }; void writeNotNull(String key, dynamic value) { @@ -50,7 +50,7 @@ Map _$HealthDataPointToJson(HealthDataPoint instance) { } } - writeNotNull('workout_summary', instance.workoutSummary); + writeNotNull('workoutSummary', instance.workoutSummary?.toJson()); writeNotNull('metadata', instance.metadata); return val; } @@ -238,7 +238,7 @@ Map _$HealthValueToJson(HealthValue instance) { NumericHealthValue _$NumericHealthValueFromJson(Map json) => NumericHealthValue( - numericValue: json['numeric_value'] as num, + numericValue: json['numericValue'] as num, )..$type = json['__type'] as String?; Map _$NumericHealthValueToJson(NumericHealthValue instance) { @@ -251,7 +251,7 @@ Map _$NumericHealthValueToJson(NumericHealthValue instance) { } writeNotNull('__type', instance.$type); - val['numeric_value'] = instance.numericValue; + val['numericValue'] = instance.numericValue; return val; } @@ -260,10 +260,10 @@ AudiogramHealthValue _$AudiogramHealthValueFromJson( AudiogramHealthValue( frequencies: (json['frequencies'] as List).map((e) => e as num).toList(), - leftEarSensitivities: (json['left_ear_sensitivities'] as List) + leftEarSensitivities: (json['leftEarSensitivities'] as List) .map((e) => e as num) .toList(), - rightEarSensitivities: (json['right_ear_sensitivities'] as List) + rightEarSensitivities: (json['rightEarSensitivities'] as List) .map((e) => e as num) .toList(), )..$type = json['__type'] as String?; @@ -280,24 +280,24 @@ Map _$AudiogramHealthValueToJson( writeNotNull('__type', instance.$type); val['frequencies'] = instance.frequencies; - val['left_ear_sensitivities'] = instance.leftEarSensitivities; - val['right_ear_sensitivities'] = instance.rightEarSensitivities; + val['leftEarSensitivities'] = instance.leftEarSensitivities; + val['rightEarSensitivities'] = instance.rightEarSensitivities; return val; } WorkoutHealthValue _$WorkoutHealthValueFromJson(Map json) => WorkoutHealthValue( workoutActivityType: $enumDecode( - _$HealthWorkoutActivityTypeEnumMap, json['workout_activity_type']), - totalEnergyBurned: (json['total_energy_burned'] as num?)?.toInt(), + _$HealthWorkoutActivityTypeEnumMap, json['workoutActivityType']), + totalEnergyBurned: (json['totalEnergyBurned'] as num?)?.toInt(), totalEnergyBurnedUnit: $enumDecodeNullable( - _$HealthDataUnitEnumMap, json['total_energy_burned_unit']), - totalDistance: (json['total_distance'] as num?)?.toInt(), + _$HealthDataUnitEnumMap, json['totalEnergyBurnedUnit']), + totalDistance: (json['totalDistance'] as num?)?.toInt(), totalDistanceUnit: $enumDecodeNullable( - _$HealthDataUnitEnumMap, json['total_distance_unit']), - totalSteps: (json['total_steps'] as num?)?.toInt(), - totalStepsUnit: $enumDecodeNullable( - _$HealthDataUnitEnumMap, json['total_steps_unit']), + _$HealthDataUnitEnumMap, json['totalDistanceUnit']), + totalSteps: (json['totalSteps'] as num?)?.toInt(), + totalStepsUnit: + $enumDecodeNullable(_$HealthDataUnitEnumMap, json['totalStepsUnit']), )..$type = json['__type'] as String?; Map _$WorkoutHealthValueToJson(WorkoutHealthValue instance) { @@ -310,17 +310,17 @@ Map _$WorkoutHealthValueToJson(WorkoutHealthValue instance) { } writeNotNull('__type', instance.$type); - val['workout_activity_type'] = + val['workoutActivityType'] = _$HealthWorkoutActivityTypeEnumMap[instance.workoutActivityType]!; - writeNotNull('total_energy_burned', instance.totalEnergyBurned); - writeNotNull('total_energy_burned_unit', + writeNotNull('totalEnergyBurned', instance.totalEnergyBurned); + writeNotNull('totalEnergyBurnedUnit', _$HealthDataUnitEnumMap[instance.totalEnergyBurnedUnit]); - writeNotNull('total_distance', instance.totalDistance); - writeNotNull('total_distance_unit', - _$HealthDataUnitEnumMap[instance.totalDistanceUnit]); - writeNotNull('total_steps', instance.totalSteps); + writeNotNull('totalDistance', instance.totalDistance); writeNotNull( - 'total_steps_unit', _$HealthDataUnitEnumMap[instance.totalStepsUnit]); + 'totalDistanceUnit', _$HealthDataUnitEnumMap[instance.totalDistanceUnit]); + writeNotNull('totalSteps', instance.totalSteps); + writeNotNull( + 'totalStepsUnit', _$HealthDataUnitEnumMap[instance.totalStepsUnit]); return val; } @@ -432,12 +432,12 @@ const _$HealthWorkoutActivityTypeEnumMap = { ElectrocardiogramHealthValue _$ElectrocardiogramHealthValueFromJson( Map json) => ElectrocardiogramHealthValue( - voltageValues: (json['voltage_values'] as List) + voltageValues: (json['voltageValues'] as List) .map((e) => ElectrocardiogramVoltageValue.fromJson(e as Map)) .toList(), - averageHeartRate: json['average_heart_rate'] as num?, - samplingFrequency: (json['sampling_frequency'] as num?)?.toDouble(), + averageHeartRate: json['averageHeartRate'] as num?, + samplingFrequency: (json['samplingFrequency'] as num?)?.toDouble(), classification: $enumDecodeNullable( _$ElectrocardiogramClassificationEnumMap, json['classification']), )..$type = json['__type'] as String?; @@ -453,9 +453,9 @@ Map _$ElectrocardiogramHealthValueToJson( } writeNotNull('__type', instance.$type); - val['voltage_values'] = instance.voltageValues; - writeNotNull('average_heart_rate', instance.averageHeartRate); - writeNotNull('sampling_frequency', instance.samplingFrequency); + val['voltageValues'] = instance.voltageValues.map((e) => e.toJson()).toList(); + writeNotNull('averageHeartRate', instance.averageHeartRate); + writeNotNull('samplingFrequency', instance.samplingFrequency); writeNotNull('classification', _$ElectrocardiogramClassificationEnumMap[instance.classification]); return val; @@ -479,7 +479,7 @@ ElectrocardiogramVoltageValue _$ElectrocardiogramVoltageValueFromJson( Map json) => ElectrocardiogramVoltageValue( voltage: json['voltage'] as num, - timeSinceSampleStart: json['time_since_sample_start'] as num, + timeSinceSampleStart: json['timeSinceSampleStart'] as num, )..$type = json['__type'] as String?; Map _$ElectrocardiogramVoltageValueToJson( @@ -494,7 +494,7 @@ Map _$ElectrocardiogramVoltageValueToJson( writeNotNull('__type', instance.$type); val['voltage'] = instance.voltage; - val['time_since_sample_start'] = instance.timeSinceSampleStart; + val['timeSinceSampleStart'] = instance.timeSinceSampleStart; return val; } @@ -531,36 +531,36 @@ NutritionHealthValue _$NutritionHealthValueFromJson( Map json) => NutritionHealthValue( name: json['name'] as String?, - mealType: json['meal_type'] as String?, + mealType: json['mealType'] as String?, calories: (json['calories'] as num?)?.toDouble(), protein: (json['protein'] as num?)?.toDouble(), fat: (json['fat'] as num?)?.toDouble(), carbs: (json['carbs'] as num?)?.toDouble(), caffeine: (json['caffeine'] as num?)?.toDouble(), - vitaminA: (json['vitamin_a'] as num?)?.toDouble(), - b1Thiamine: (json['b1_thiamine'] as num?)?.toDouble(), - b2Riboflavin: (json['b2_riboflavin'] as num?)?.toDouble(), - b3Niacin: (json['b3_niacin'] as num?)?.toDouble(), - b5PantothenicAcid: (json['b5_pantothenic_acid'] as num?)?.toDouble(), - b6Pyridoxine: (json['b6_pyridoxine'] as num?)?.toDouble(), - b7Biotin: (json['b7_biotin'] as num?)?.toDouble(), - b9Folate: (json['b9_folate'] as num?)?.toDouble(), - b12Cobalamin: (json['b12_cobalamin'] as num?)?.toDouble(), - vitaminC: (json['vitamin_c'] as num?)?.toDouble(), - vitaminD: (json['vitamin_d'] as num?)?.toDouble(), - vitaminE: (json['vitamin_e'] as num?)?.toDouble(), - vitaminK: (json['vitamin_k'] as num?)?.toDouble(), + vitaminA: (json['vitaminA'] as num?)?.toDouble(), + b1Thiamine: (json['b1Thiamine'] as num?)?.toDouble(), + b2Riboflavin: (json['b2Riboflavin'] as num?)?.toDouble(), + b3Niacin: (json['b3Niacin'] as num?)?.toDouble(), + b5PantothenicAcid: (json['b5PantothenicAcid'] as num?)?.toDouble(), + b6Pyridoxine: (json['b6Pyridoxine'] as num?)?.toDouble(), + b7Biotin: (json['b7Biotin'] as num?)?.toDouble(), + b9Folate: (json['b9Folate'] as num?)?.toDouble(), + b12Cobalamin: (json['b12Cobalamin'] as num?)?.toDouble(), + vitaminC: (json['vitaminC'] as num?)?.toDouble(), + vitaminD: (json['vitaminD'] as num?)?.toDouble(), + vitaminE: (json['vitaminE'] as num?)?.toDouble(), + vitaminK: (json['vitaminK'] as num?)?.toDouble(), calcium: (json['calcium'] as num?)?.toDouble(), chloride: (json['chloride'] as num?)?.toDouble(), cholesterol: (json['cholesterol'] as num?)?.toDouble(), choline: (json['choline'] as num?)?.toDouble(), chromium: (json['chromium'] as num?)?.toDouble(), copper: (json['copper'] as num?)?.toDouble(), - fatUnsaturated: (json['fat_unsaturated'] as num?)?.toDouble(), - fatMonounsaturated: (json['fat_monounsaturated'] as num?)?.toDouble(), - fatPolyunsaturated: (json['fat_polyunsaturated'] as num?)?.toDouble(), - fatSaturated: (json['fat_saturated'] as num?)?.toDouble(), - fatTransMonoenoic: (json['fat_trans_monoenoic'] as num?)?.toDouble(), + fatUnsaturated: (json['fatUnsaturated'] as num?)?.toDouble(), + fatMonounsaturated: (json['fatMonounsaturated'] as num?)?.toDouble(), + fatPolyunsaturated: (json['fatPolyunsaturated'] as num?)?.toDouble(), + fatSaturated: (json['fatSaturated'] as num?)?.toDouble(), + fatTransMonoenoic: (json['fatTransMonoenoic'] as num?)?.toDouble(), fiber: (json['fiber'] as num?)?.toDouble(), iodine: (json['iodine'] as num?)?.toDouble(), iron: (json['iron'] as num?)?.toDouble(), @@ -588,36 +588,36 @@ Map _$NutritionHealthValueToJson( writeNotNull('__type', instance.$type); writeNotNull('name', instance.name); - writeNotNull('meal_type', instance.mealType); + writeNotNull('mealType', instance.mealType); writeNotNull('calories', instance.calories); writeNotNull('protein', instance.protein); writeNotNull('fat', instance.fat); writeNotNull('carbs', instance.carbs); writeNotNull('caffeine', instance.caffeine); - writeNotNull('vitamin_a', instance.vitaminA); - writeNotNull('b1_thiamine', instance.b1Thiamine); - writeNotNull('b2_riboflavin', instance.b2Riboflavin); - writeNotNull('b3_niacin', instance.b3Niacin); - writeNotNull('b5_pantothenic_acid', instance.b5PantothenicAcid); - writeNotNull('b6_pyridoxine', instance.b6Pyridoxine); - writeNotNull('b7_biotin', instance.b7Biotin); - writeNotNull('b9_folate', instance.b9Folate); - writeNotNull('b12_cobalamin', instance.b12Cobalamin); - writeNotNull('vitamin_c', instance.vitaminC); - writeNotNull('vitamin_d', instance.vitaminD); - writeNotNull('vitamin_e', instance.vitaminE); - writeNotNull('vitamin_k', instance.vitaminK); + writeNotNull('vitaminA', instance.vitaminA); + writeNotNull('b1Thiamine', instance.b1Thiamine); + writeNotNull('b2Riboflavin', instance.b2Riboflavin); + writeNotNull('b3Niacin', instance.b3Niacin); + writeNotNull('b5PantothenicAcid', instance.b5PantothenicAcid); + writeNotNull('b6Pyridoxine', instance.b6Pyridoxine); + writeNotNull('b7Biotin', instance.b7Biotin); + writeNotNull('b9Folate', instance.b9Folate); + writeNotNull('b12Cobalamin', instance.b12Cobalamin); + writeNotNull('vitaminC', instance.vitaminC); + writeNotNull('vitaminD', instance.vitaminD); + writeNotNull('vitaminE', instance.vitaminE); + writeNotNull('vitaminK', instance.vitaminK); writeNotNull('calcium', instance.calcium); writeNotNull('chloride', instance.chloride); writeNotNull('cholesterol', instance.cholesterol); writeNotNull('choline', instance.choline); writeNotNull('chromium', instance.chromium); writeNotNull('copper', instance.copper); - writeNotNull('fat_unsaturated', instance.fatUnsaturated); - writeNotNull('fat_monounsaturated', instance.fatMonounsaturated); - writeNotNull('fat_polyunsaturated', instance.fatPolyunsaturated); - writeNotNull('fat_saturated', instance.fatSaturated); - writeNotNull('fat_trans_monoenoic', instance.fatTransMonoenoic); + writeNotNull('fatUnsaturated', instance.fatUnsaturated); + writeNotNull('fatMonounsaturated', instance.fatMonounsaturated); + writeNotNull('fatPolyunsaturated', instance.fatPolyunsaturated); + writeNotNull('fatSaturated', instance.fatSaturated); + writeNotNull('fatTransMonoenoic', instance.fatTransMonoenoic); writeNotNull('fiber', instance.fiber); writeNotNull('iodine', instance.iodine); writeNotNull('iron', instance.iron); @@ -638,9 +638,9 @@ MenstruationFlowHealthValue _$MenstruationFlowHealthValueFromJson( Map json) => MenstruationFlowHealthValue( flow: $enumDecodeNullable(_$MenstrualFlowEnumMap, json['flow']), - dateTime: DateTime.parse(json['date_time'] as String), - isStartOfCycle: json['is_start_of_cycle'] as bool?, - wasUserEntered: json['was_user_entered'] as bool?, + dateTime: DateTime.parse(json['dateTime'] as String), + isStartOfCycle: json['isStartOfCycle'] as bool?, + wasUserEntered: json['wasUserEntered'] as bool?, )..$type = json['__type'] as String?; Map _$MenstruationFlowHealthValueToJson( @@ -655,9 +655,9 @@ Map _$MenstruationFlowHealthValueToJson( writeNotNull('__type', instance.$type); writeNotNull('flow', _$MenstrualFlowEnumMap[instance.flow]); - writeNotNull('is_start_of_cycle', instance.isStartOfCycle); - writeNotNull('was_user_entered', instance.wasUserEntered); - val['date_time'] = instance.dateTime.toIso8601String(); + writeNotNull('isStartOfCycle', instance.isStartOfCycle); + writeNotNull('wasUserEntered', instance.wasUserEntered); + val['dateTime'] = instance.dateTime.toIso8601String(); return val; } @@ -672,16 +672,16 @@ const _$MenstrualFlowEnumMap = { WorkoutSummary _$WorkoutSummaryFromJson(Map json) => WorkoutSummary( - workoutType: json['workout_type'] as String, - totalDistance: json['total_distance'] as num, - totalEnergyBurned: json['total_energy_burned'] as num, - totalSteps: json['total_steps'] as num, + workoutType: json['workoutType'] as String, + totalDistance: json['totalDistance'] as num, + totalEnergyBurned: json['totalEnergyBurned'] as num, + totalSteps: json['totalSteps'] as num, ); Map _$WorkoutSummaryToJson(WorkoutSummary instance) => { - 'workout_type': instance.workoutType, - 'total_distance': instance.totalDistance, - 'total_energy_burned': instance.totalEnergyBurned, - 'total_steps': instance.totalSteps, + 'workoutType': instance.workoutType, + 'totalDistance': instance.totalDistance, + 'totalEnergyBurned': instance.totalEnergyBurned, + 'totalSteps': instance.totalSteps, }; diff --git a/packages/health/lib/src/health_data_point.dart b/packages/health/lib/src/health_data_point.dart index 2598a0e1b..029af835c 100644 --- a/packages/health/lib/src/health_data_point.dart +++ b/packages/health/lib/src/health_data_point.dart @@ -6,7 +6,7 @@ enum HealthPlatformType { appleHealth, googleHealthConnect } /// A [HealthDataPoint] object corresponds to a data point capture from /// Apple HealthKit or Google Health Connect with a [HealthValue] /// as value. -@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) +@JsonSerializable(includeIfNull: false, explicitToJson: true) class HealthDataPoint { /// UUID of the data point. String uuid; diff --git a/packages/health/lib/src/health_value_types.dart b/packages/health/lib/src/health_value_types.dart index d313104ca..4b88adc50 100644 --- a/packages/health/lib/src/health_value_types.dart +++ b/packages/health/lib/src/health_value_types.dart @@ -1,14 +1,14 @@ part of '../health.dart'; /// An abstract class for health values. -@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) +@JsonSerializable(includeIfNull: false, explicitToJson: true) class HealthValue extends Serializable { HealthValue(); @override Function get fromJsonFunction => _$HealthValueFromJson; factory HealthValue.fromJson(Map json) => - FromJsonFactory().fromJson(json) as HealthValue; + FromJsonFactory().fromJson(json); @override Map toJson() => _$HealthValueToJson(this); } @@ -18,7 +18,7 @@ class HealthValue extends Serializable { /// /// Parameters: /// * [numericValue] - a [num] value for the [HealthDataPoint] -@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) +@JsonSerializable(includeIfNull: false, explicitToJson: true) class NumericHealthValue extends HealthValue { /// A [num] value for the [HealthDataPoint]. num numericValue; @@ -35,7 +35,7 @@ class NumericHealthValue extends HealthValue { @override Function get fromJsonFunction => _$NumericHealthValueFromJson; factory NumericHealthValue.fromJson(Map json) => - FromJsonFactory().fromJson(json) as NumericHealthValue; + FromJsonFactory().fromJson(json); @override Map toJson() => _$NumericHealthValueToJson(this); @@ -53,7 +53,7 @@ class NumericHealthValue extends HealthValue { /// * [frequencies] - array of frequencies of the test /// * [leftEarSensitivities] threshold in decibel for the left ear /// * [rightEarSensitivities] threshold in decibel for the left ear -@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) +@JsonSerializable(includeIfNull: false, explicitToJson: true) class AudiogramHealthValue extends HealthValue { /// Array of frequencies of the test. List frequencies; @@ -87,7 +87,7 @@ class AudiogramHealthValue extends HealthValue { @override Function get fromJsonFunction => _$AudiogramHealthValueFromJson; factory AudiogramHealthValue.fromJson(Map json) => - FromJsonFactory().fromJson(json) as AudiogramHealthValue; + FromJsonFactory().fromJson(json); @override Map toJson() => _$AudiogramHealthValueToJson(this); @@ -111,7 +111,7 @@ class AudiogramHealthValue extends HealthValue { /// * [totalEnergyBurnedUnit] - the unit of the total energy burned /// * [totalDistance] - the total distance of the workout /// * [totalDistanceUnit] - the unit of the total distance -@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) +@JsonSerializable(includeIfNull: false, explicitToJson: true) class WorkoutHealthValue extends HealthValue { /// The type of the workout. HealthWorkoutActivityType workoutActivityType; @@ -181,7 +181,7 @@ class WorkoutHealthValue extends HealthValue { @override Function get fromJsonFunction => _$WorkoutHealthValueFromJson; factory WorkoutHealthValue.fromJson(Map json) => - FromJsonFactory().fromJson(json) as WorkoutHealthValue; + FromJsonFactory().fromJson(json); @override Map toJson() => _$WorkoutHealthValueToJson(this); @@ -224,7 +224,7 @@ class WorkoutHealthValue extends HealthValue { /// * [averageHeartRate] - the average heart rate during the ECG (in BPM) /// * [samplingFrequency] - the frequency at which the Apple Watch sampled the voltage. /// * [classification] - an [ElectrocardiogramClassification] -@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) +@JsonSerializable(includeIfNull: false, explicitToJson: true) class ElectrocardiogramHealthValue extends HealthValue { /// An array of [ElectrocardiogramVoltageValue]s. List voltageValues; @@ -248,7 +248,7 @@ class ElectrocardiogramHealthValue extends HealthValue { @override Function get fromJsonFunction => _$ElectrocardiogramHealthValueFromJson; factory ElectrocardiogramHealthValue.fromJson(Map json) => - FromJsonFactory().fromJson(json) as ElectrocardiogramHealthValue; + FromJsonFactory().fromJson(json); @override Map toJson() => _$ElectrocardiogramHealthValueToJson(this); @@ -283,7 +283,7 @@ class ElectrocardiogramHealthValue extends HealthValue { } /// Single voltage value belonging to a [ElectrocardiogramHealthValue] -@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) +@JsonSerializable(includeIfNull: false, explicitToJson: true) class ElectrocardiogramVoltageValue extends HealthValue { /// Voltage of the ECG. num voltage; @@ -306,7 +306,7 @@ class ElectrocardiogramVoltageValue extends HealthValue { @override Function get fromJsonFunction => _$ElectrocardiogramVoltageValueFromJson; factory ElectrocardiogramVoltageValue.fromJson(Map json) => - FromJsonFactory().fromJson(json) as ElectrocardiogramVoltageValue; + FromJsonFactory().fromJson(json); @override Map toJson() => _$ElectrocardiogramVoltageValueToJson(this); @@ -324,7 +324,7 @@ class ElectrocardiogramVoltageValue extends HealthValue { } /// A [HealthValue] object from insulin delivery (iOS only) -@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) +@JsonSerializable(includeIfNull: false, explicitToJson: true) class InsulinDeliveryHealthValue extends HealthValue { /// The amount of units of insulin taken double units; @@ -355,7 +355,7 @@ class InsulinDeliveryHealthValue extends HealthValue { @override Function get fromJsonFunction => _$InsulinDeliveryHealthValueFromJson; factory InsulinDeliveryHealthValue.fromJson(Map json) => - FromJsonFactory().fromJson(json) as InsulinDeliveryHealthValue; + FromJsonFactory().fromJson(json); @override Map toJson() => _$InsulinDeliveryHealthValueToJson(this); @@ -420,7 +420,7 @@ class InsulinDeliveryHealthValue extends HealthValue { /// * [water] - the amount of water in grams /// * [zinc] - the amount of zinc in grams -@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) +@JsonSerializable(includeIfNull: false, explicitToJson: true) class NutritionHealthValue extends HealthValue { /// The name of the food. String? name; @@ -604,7 +604,7 @@ class NutritionHealthValue extends HealthValue { @override Function get fromJsonFunction => _$NutritionHealthValueFromJson; factory NutritionHealthValue.fromJson(Map json) => - FromJsonFactory().fromJson(json) as NutritionHealthValue; + (json) as NutritionHealthValue; @override Map toJson() => _$NutritionHealthValueToJson(this); @@ -865,7 +865,7 @@ enum RecordingMethod { /// * [isStartOfCycle] - indicator whether or not this occurrence is the first day of the menstrual cycle (iOS only) /// * [wasUserEntered] - indicator whether or not the data was entered by the user (iOS only) /// * [dateTime] - the date and time of the menstrual flow -@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) +@JsonSerializable(includeIfNull: false, explicitToJson: true) class MenstruationFlowHealthValue extends HealthValue { final MenstrualFlow? flow; final bool? isStartOfCycle; @@ -912,7 +912,7 @@ class MenstruationFlowHealthValue extends HealthValue { Function get fromJsonFunction => _$MenstruationFlowHealthValueFromJson; factory MenstruationFlowHealthValue.fromJson(Map json) => - FromJsonFactory().fromJson(json) as MenstruationFlowHealthValue; + FromJsonFactory().fromJson(json); @override Map toJson() => _$MenstruationFlowHealthValueToJson(this); diff --git a/packages/health/lib/src/workout_summary.dart b/packages/health/lib/src/workout_summary.dart index c36613802..14682a402 100644 --- a/packages/health/lib/src/workout_summary.dart +++ b/packages/health/lib/src/workout_summary.dart @@ -6,7 +6,7 @@ part of '../health.dart'; /// * [totalDistance] - The total distance that was traveled during a workout. /// * [totalEnergyBurned] - The amount of energy that was burned during a workout. /// * [totalSteps] - The number of steps during a workout. -@JsonSerializable(fieldRename: FieldRename.snake, includeIfNull: false) +@JsonSerializable(includeIfNull: false, explicitToJson: true) class WorkoutSummary { /// Workout type. String workoutType; diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index ffc1be0af..56339aba8 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: intl: '>=0.18.0 <0.20.0' device_info_plus: '>=9.0.0 <11.0.0' json_annotation: ^4.8.0 - carp_serializable: ^1.1.0 # polymorphic json serialization + carp_serializable: ^2.0.0 # polymorphic json serialization dev_dependencies: flutter_test: From 27ac35d032d0cf013db1d18c6835b9791084a4bb Mon Sep 17 00:00:00 2001 From: bardram Date: Mon, 23 Sep 2024 16:46:54 +0200 Subject: [PATCH 34/80] Update pubspec.yaml --- packages/health/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index 56339aba8..7690d1c50 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -1,6 +1,6 @@ name: health description: Wrapper for Apple's HealthKit on iOS and Google's Health Connect on Android. -version: 11.0.0 +version: 11.1.0 homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages/health environment: From 0b89660e5cf45a1cedbcec6f7af34010402f028a Mon Sep 17 00:00:00 2001 From: avargas-btf Date: Wed, 25 Sep 2024 18:09:04 -0500 Subject: [PATCH 35/80] Register WorkoutHealthValue --- packages/health/lib/health.json.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/health/lib/health.json.dart b/packages/health/lib/health.json.dart index 351098a28..2162869da 100644 --- a/packages/health/lib/health.json.dart +++ b/packages/health/lib/health.json.dart @@ -10,6 +10,7 @@ void _registerFromJsonFunctions() { FromJsonFactory().registerAll([ HealthValue(), NumericHealthValue(numericValue: 12), + WorkoutHealthValue(workoutActivityType: HealthWorkoutActivityType.RUNNING), AudiogramHealthValue( frequencies: [], leftEarSensitivities: [], From e51bcaba1819ebf6e1d5cbcfda23290ba608e076 Mon Sep 17 00:00:00 2001 From: bardram Date: Thu, 26 Sep 2024 11:54:12 +0200 Subject: [PATCH 36/80] mobility_features 6.0.0 --- packages/mobility_features/CHANGELOG.md | 116 +++++----- packages/mobility_features/README.md | 57 ++--- .../example/android/app/build.gradle | 63 ++---- .../android/app/src/main/AndroidManifest.xml | 1 - .../example/android/build.gradle | 13 -- .../example/android/settings.gradle | 32 ++- .../mobility_features/example/lib/main.dart | 97 ++++---- .../example/lib/moves_page.dart | 6 +- .../example/lib/places_page.dart | 6 +- .../example/lib/stops_page.dart | 6 +- .../mobility_features/example/pubspec.yaml | 6 +- .../mobility_features/images/Untitled.drawio | 1 - packages/mobility_features/images/app.jpeg | Bin 0 -> 439116 bytes .../mobility_features/images/features.jpeg | Bin 66291 -> 29631 bytes packages/mobility_features/images/moves.jpeg | Bin 81519 -> 35622 bytes packages/mobility_features/images/places.jpeg | Bin 74164 -> 32661 bytes packages/mobility_features/images/stops.jpeg | Bin 100425 -> 43440 bytes .../lib/mobility_features.dart | 17 +- .../lib/mobility_features.g.dart | 155 +++++++++++++ .../mobility_features/lib/src/domain.dart | 213 +++++++----------- .../mobility_features/lib/src/file_util.dart | 42 ---- .../lib/src/{features.dart => main.dart} | 87 ++++--- .../{context.dart => mobility_context.dart} | 173 +++++++------- ...ermediate.dart => mobility_functions.dart} | 78 +++---- .../mobility_features/lib/src/serializer.dart | 52 ++++- .../lib/src/{functions.dart => util.dart} | 0 .../mobility_features_example/ios/Podfile | 41 ---- packages/mobility_features/pubspec.yaml | 6 +- .../test/mobility_features_test.dart | 28 +-- .../test/testdata/location_samples.json | 110 --------- .../test/testdata/moves.json | 50 ++-- .../test/testdata/stops.json | 52 ++--- 32 files changed, 723 insertions(+), 785 deletions(-) delete mode 100644 packages/mobility_features/images/Untitled.drawio create mode 100644 packages/mobility_features/images/app.jpeg create mode 100644 packages/mobility_features/lib/mobility_features.g.dart delete mode 100644 packages/mobility_features/lib/src/file_util.dart rename packages/mobility_features/lib/src/{features.dart => main.dart} (79%) rename packages/mobility_features/lib/src/{context.dart => mobility_context.dart} (51%) rename packages/mobility_features/lib/src/{intermediate.dart => mobility_functions.dart} (99%) rename packages/mobility_features/lib/src/{functions.dart => util.dart} (100%) delete mode 100644 packages/mobility_features/mobility_features_example/ios/Podfile diff --git a/packages/mobility_features/CHANGELOG.md b/packages/mobility_features/CHANGELOG.md index 5baba2452..aa09baeed 100644 --- a/packages/mobility_features/CHANGELOG.md +++ b/packages/mobility_features/CHANGELOG.md @@ -1,120 +1,126 @@ +## 6.0.0 + +* using carp_serialization for JSON serialization resulting in a new JSON schema for serialization +* upgrade of example app to use new Flutter Android Gradle build setup +* improvements of code structure and documentation + ## 5.0.0 -- upgrade to Dart 3.2 and Flutter 3.16 -- improvements to API docs -- improvements to example apps (permissions - PR [#971](https://github.com/cph-cachet/flutter-plugins/pull/971)). +* upgrade to Dart 3.2 and Flutter 3.16 +* improvements to API docs +* improvements to example apps (permissions - PR [#971](https://github.com/cph-cachet/flutter-plugins/pull/971)). ## 4.0.1 -- Fixed formatting -- Lowered minSdk +* Fixed formatting +* Lowered minSdk ## 4.0.0 -- Updated kotlin and AGP -- Upgraded example app to `carp_background_location` 4.0.0 -- Implemented minor fixes +* Updated kotlin and AGP +* Upgraded example app to `carp_background_location` 4.0.0 +* Implemented minor fixes ## 3.1.0 -- improvement to `MobilityContext` API. -- misc updates to documentation. +* improvement to `MobilityContext` API. +* misc updates to documentation. ## 3.0.0+2 -- update to null-safety -- rename of `MobilityFactory` to `MobilityFeatures`and using the standard Dart singleton syntax for `MobilityFeatures()`. -- misc updates to documentation +* update to null-safety +* rename of `MobilityFactory` to `MobilityFeatures`and using the standard Dart singleton syntax for `MobilityFeatures()`. +* misc updates to documentation ## 2.0.6+1 -- removal of exception -- update to use `carp_background_location` +* removal of exception +* update to use `carp_background_location` ## 2.0.5 -- Documentation -- Added images and changed documentation somewhat +* Documentation +* Added images and changed documentation somewhat ## 2.0.4 -- Move Consolidation -- Moves are now recomputed each time a stop is computed -- This means that the number of moves is always one less that the number of stops +* Move Consolidation +* Moves are now recomputed each time a stop is computed +* This means that the number of moves is always one less that the number of stops ## 2.0.3 -- Move Calculation -- Fixed a bug when creating moves between two stops belonging to the same place -- To avoid inaccuracy distance as a resulting of noisy readings when inside buildings, this path should be computed as a straight line, rather than from a list of points +* Move Calculation +* Fixed a bug when creating moves between two stops belonging to the same place +* To avoid inaccuracy distance as a resulting of noisy readings when inside buildings, this path should be computed as a straight line, rather than from a list of points ## 2.0.2 -- Stop merging -- Implemented stop merging to prevent gaps in the data -- This was especially a problem on iOS devices during the night, where location sampling is automatically limited by the OS -- Gaps in the data during the night would cause the home stay feature to be very unreliable +* Stop merging +* Implemented stop merging to prevent gaps in the data +* This was especially a problem on iOS devices during the night, where location sampling is automatically limited by the OS +* Gaps in the data during the night would cause the home stay feature to be very unreliable ## 2.0.1 -- Stream-based API -- Removed Routine Index temporarily +* Stream-based API +* Removed Routine Index temporarily ## 2.0.0 -- Stream-based API -- The API is now fully streaming-based. +* Stream-based API +* The API is now fully streaming-based. ## 1.3.4 -- Flushing data -- Fixed an error where location samples were being flushed when they shouldn't +* Flushing data +* Fixed an error where location samples were being flushed when they shouldn't ## 1.3.3 -- Dependencies -- Updated dependencies +* Dependencies +* Updated dependencies ## 1.3.2 -- Streaming based API -- Renamed GeoPosition to GeoLocation due to naming conflicts with another package. +* Streaming based API +* Renamed GeoPosition to GeoLocation due to naming conflicts with another package. ## 1.3.0 -- Streaming based API -- Refactored API to support streaming -- An example app is now included +* Streaming based API +* Refactored API to support streaming +* An example app is now included ## 1.2.0 -- Restructuring -- MobilitySerializer is now private. +* Restructuring +* MobilitySerializer is now private. ## 1.1.5 -- Major refactoring -- Renamed and refactored classes such as Location and SingleLocationPoint to GeoPosition and LocationSample respectively. +* Major refactoring +* Renamed and refactored classes such as Location and SingleLocationPoint to GeoPosition and LocationSample respectively. ## 1.1.0 -- Private classes -- Made a series of classes private such that they cannot be instantiated from outside the package +* Private classes +* Made a series of classes private such that they cannot be instantiated from outside the package ## 1.0.0 -- Formatting -- Fixed a series of formatting issues which caused the package to score lower on pub.dev -- Upgraded the release number to 1.x.x to increase the package score on pub.dev +* Formatting +* Fixed a series of formatting issues which caused the package to score lower on pub.dev +* Upgraded the release number to 1.x.x to increase the package score on pub.dev ## 0.1.5 -- Private constructor. -- The Mobility Context constructor is now private -- A Mobility Context should always be instantiated via the ContextGenerator class. +* Private constructor. +* The Mobility Context constructor is now private +* A Mobility Context should always be instantiated via the ContextGenerator class. ## 0.1.0 -- First release. -- The first official release with working unit tests -- Includes a minimalistic API which allows the application programmer to generate features with very few lines of code. +* First release. +* The first official release with working unit tests +* Includes a minimalistic API which allows the application programmer to generate features with very few lines of code. diff --git a/packages/mobility_features/README.md b/packages/mobility_features/README.md index 85d5be2a6..c3196acb6 100644 --- a/packages/mobility_features/README.md +++ b/packages/mobility_features/README.md @@ -21,7 +21,7 @@ Read more on the [theoretical background](#theoretical-background) on these mobi The Mobility Features package is designed to work independent of the location plugin. You may choose you own location plugin, since you may already use this in your app. -In the example app we use our own plugin [`carp_background_location`](https://pub.dev/packages/carp_background_location) which works on both Android and iOS as of August 2020. However, the [location](https://pub.dev/packages/location) plugin will also work. The important thing, however, is to make sure that the app runs in the background. On Android this is tied to running the app as a foreground service. +In the example app we use our own plugin [carp_background_location](https://pub.dev/packages/carp_background_location) which works on both Android and iOS as of August 2020. However, the [location](https://pub.dev/packages/location) plugin will also work. The important thing, however, is to make sure that the app runs in the background. On Android this is tied to running the app as a foreground service. Add the package to your `pubspec.yaml` file and import the package @@ -62,22 +62,18 @@ For example, given a low stop duration, stopping for a red light in traffic will ### Step 2 - Set up location streaming -Collection of location data is not directly supported by this package, for this you have to use a location plugin such as [`carp_background_location`](https://pub.dev/packages/carp_background_location). You can to convert from whichever location object is used by the location plugin to a `LocationSample` object. -Next, you can start listening to location updates and subscribe to the `MobilityFeatures()`'s `contextStream` to be be notified each time a new set of features has been computed. +Collection of location data is not directly supported by this package, for this you have to use a location plugin such as [carp_background_location](https://pub.dev/packages/carp_background_location). You can to convert from whichever location object is used by the location plugin to a `LocationSample` object. +Next, you can start listening to location updates and subscribe to the `contextStream` to be be notified each time a new set of features has been computed. -Below is shown an example using the [`carp_background_location`](https://pub.dev/packages/carp_background_location) plugin, where a `LocationDto` stream is converted into a `LocationSample` stream by using a map-function. +Below is shown an example using the [carp_background_location](https://pub.dev/packages/carp_background_location) plugin, where a `LocationDto` stream is converted into a `LocationSample` stream by using a map-function. ```dart /// Set up streams: - /// * Location streaming to MobilityContext - /// * Subscribe to MobilityContext updates + /// * Location streaming to MobilityContext + /// * Subscribe to MobilityContext updates void streamInit() async { locationStream = LocationManager().locationStream; - // subscribe to location stream - in case this is needed in the app - if (locationSubscription != null) locationSubscription.cancel(); - locationSubscription = locationStream.listen(onLocationUpdate); - // start the location service (specific to carp_background_location) await LocationManager().start(); @@ -94,14 +90,9 @@ Below is shown an example using the [`carp_background_location`](https://pub.dev mobilitySubscription = MobilityFeatures().contextStream.listen(onMobilityContext); } - - /// Called whenever location changes. - void onLocationUpdate(LocationDto dto) { - print(dtoToString(dto)); - } ``` -> **Note** that access to location data needs permissions from the OS. This is **not** handled by the plugin but should be handled on an app-level. See the example app for this. Note also, that permissions for access location "ALWAYS" needs to be granted by the user in order to collect location information in the background. +> **NOTE** that access to location data needs permissions from the OS. This is **NOT** handled by the plugin but should be handled on an app-level. See the example app for this. Note also, that permissions for access location "ALWAYS" needs to be granted by the user in order to collect location information in the background. ### Step 3 - Listen to mobility features @@ -115,20 +106,21 @@ void onMobilityContext(MobilityContext context) { } ``` -All the mobility features are accessible in the `MobilityContext` object: - -```dart -/// Location features -context.places; -context.stops; -context.moves; - -/// Derived features -context.numberOfSignificantPlaces; -context.homeStay; -context.entropy; -context.normalizedEntropy; -context.distanceTraveled; +Mobility features are accessible in the `MobilityContext` object which can be serialized to JSON using the `toJson()` method: + +```json +{ + "timestamp": "2024-09-26T10:56:21.397768", + "date": "2020-01-01T00:00:00.000", + "numberOfStops": 2, + "numberOfMoves": 1, + "numberOfSignificantPlaces": 2, + "locationVariance": 0.00011097661986704458, + "entropy": 0.6365141682948128, + "normalizedEntropy": 0.9182958340544894, + "homeStay": 0.64, + "distanceTraveled": 0.0 +} ``` ## Feature errors @@ -145,10 +137,7 @@ For example: The example application included in the package shows the feature values, including separate pages for stops, moves and places. It also illustrates how to ask the user for permissions to access location data, also when the app is in the background. -![mobility_app_1](https://raw.githubusercontent.com/cph-cachet/flutter-plugins/master/packages/mobility_features/images/features.jpeg) -![mobility_app_2](https://raw.githubusercontent.com/cph-cachet/flutter-plugins/master/packages/mobility_features/images/stops.jpeg) -![mobility_app_3](https://raw.githubusercontent.com/cph-cachet/flutter-plugins/master/packages/mobility_features/images/places.jpeg) -![mobility_app_4](https://raw.githubusercontent.com/cph-cachet/flutter-plugins/master/packages/mobility_features/images/moves.jpeg) +![mobility_app_1](https://raw.githubusercontent.com/cph-cachet/flutter-plugins/master/packages/mobility_features/images/app.jpeg) ## Theoretical Background diff --git a/packages/mobility_features/example/android/app/build.gradle b/packages/mobility_features/example/android/app/build.gradle index e5ef93d83..982ab50c7 100644 --- a/packages/mobility_features/example/android/app/build.gradle +++ b/packages/mobility_features/example/android/app/build.gradle @@ -1,63 +1,44 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" } -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { - compileSdkVersion 33 + namespace = "com.example.example" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion - sourceSets { - main.java.srcDirs += 'src/main/kotlin' + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } - lintOptions { - disable 'InvalidPackage' + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8 } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.example" - minSdkVersion 23 - targetSdkVersion 33 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName + applicationId = "com.example.example" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = 23 + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName } buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug + signingConfig = signingConfigs.debug } } } flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + source = "../.." } diff --git a/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml b/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml index 5a00b5546..cf2dc551e 100644 --- a/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml +++ b/packages/mobility_features/example/android/app/src/main/AndroidManifest.xml @@ -7,7 +7,6 @@ FlutterApplication and put your custom class here. --> - diff --git a/packages/mobility_features/example/android/build.gradle b/packages/mobility_features/example/android/build.gradle index f7eb7f63c..bc157bd1a 100644 --- a/packages/mobility_features/example/android/build.gradle +++ b/packages/mobility_features/example/android/build.gradle @@ -1,16 +1,3 @@ -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() diff --git a/packages/mobility_features/example/android/settings.gradle b/packages/mobility_features/example/android/settings.gradle index d3b6a4013..536165d35 100644 --- a/packages/mobility_features/example/android/settings.gradle +++ b/packages/mobility_features/example/android/settings.gradle @@ -1,15 +1,25 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -include ':app' + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +include ":app" diff --git a/packages/mobility_features/example/lib/main.dart b/packages/mobility_features/example/lib/main.dart index 61d9944d3..f7b42181d 100644 --- a/packages/mobility_features/example/lib/main.dart +++ b/packages/mobility_features/example/lib/main.dart @@ -10,12 +10,12 @@ part 'stops_page.dart'; part 'moves_page.dart'; part 'places_page.dart'; -void main() => runApp(MyApp()); +void main() => runApp(const MyApp()); Widget entry(String key, String value, Icon icon) { return Container( padding: const EdgeInsets.all(2), - margin: EdgeInsets.all(3), + margin: const EdgeInsets.all(3), child: ListTile( leading: icon, title: Text(key), @@ -26,7 +26,7 @@ Widget entry(String key, String value, Icon icon) { String formatDate(DateTime date) => '${date.year}/${date.month}/${date.day}'; String interval(DateTime a, DateTime b) { - String pad(int x) => '${x.toString().padLeft(2, '0')}'; + String pad(int x) => x.toString().padLeft(2, '0'); return '${pad(a.hour)}:${pad(a.minute)}:${pad(a.second)} - ${pad(b.hour)}:${pad(b.minute)}:${pad(b.second)}'; } @@ -37,18 +37,20 @@ String formatDuration(Duration duration) { return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds"; } -final stopIcon = Icon(Icons.my_location); -final moveIcon = Icon(Icons.directions_walk); -final placeIcon = Icon(Icons.place); -final featuresIcon = Icon(Icons.assessment); -final homeStayIcon = Icon(Icons.home); -final distanceTraveledIcon = Icon(Icons.card_travel); -final entropyIcon = Icon(Icons.equalizer); -final varianceIcon = Icon(Icons.swap_calls); +const stopIcon = Icon(Icons.my_location); +const moveIcon = Icon(Icons.directions_walk); +const placeIcon = Icon(Icons.place); +const featuresIcon = Icon(Icons.assessment); +const homeStayIcon = Icon(Icons.home); +const distanceTraveledIcon = Icon(Icons.card_travel); +const entropyIcon = Icon(Icons.equalizer); +const varianceIcon = Icon(Icons.swap_calls); enum AppState { NO_FEATURES, CALCULATING_FEATURES, FEATURES_READY } class MyApp extends StatelessWidget { + const MyApp({super.key}); + @override Widget build(BuildContext context) { return MaterialApp( @@ -56,7 +58,7 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, ), - home: MyHomePage(title: 'Mobility Features Example'), + home: const HomePage(title: 'Mobility Features Example'), ); } } @@ -64,15 +66,15 @@ class MyApp extends StatelessWidget { String dtoToString(LocationDto dto) => '${dto.latitude}, ${dto.longitude} @ ${DateTime.fromMillisecondsSinceEpoch(dto.time ~/ 1)}'; -class MyHomePage extends StatefulWidget { - MyHomePage({Key? key, required this.title}) : super(key: key); +class HomePage extends StatefulWidget { + const HomePage({super.key, required this.title}); final String title; @override - _MyHomePageState createState() => _MyHomePageState(); + HomePageState createState() => HomePageState(); } -class _MyHomePageState extends State { +class HomePageState extends State { AppState _state = AppState.NO_FEATURES; int _currentIndex = 0; @@ -90,7 +92,7 @@ class _MyHomePageState extends State { super.initState(); // Set up Mobility Features - MobilityFeatures().stopDuration = Duration(seconds: 20); + MobilityFeatures().stopDuration = const Duration(seconds: 20); MobilityFeatures().placeRadius = 50.0; MobilityFeatures().stopRadius = 5.0; @@ -103,11 +105,10 @@ class _MyHomePageState extends State { } /// Set up streams: - /// * Location streaming to MobilityContext - /// * Subscribe to MobilityContext updates + /// * Location streaming to MobilityContext + /// * Subscribe to MobilityContext updates Future streamInit() async { await requestNotificationPermission(); - await requestManageExternalStoragePermission(); // ask for location permissions, if not already granted if (!await isLocationAlwaysGranted()) { @@ -117,10 +118,6 @@ class _MyHomePageState extends State { locationStream = LocationManager().locationStream; - // subscribe to location stream - in case this is needed in the app - //locationSubscription.cancel(); - locationSubscription = locationStream.listen(onLocationUpdate); - // start the location service (specific to carp_background_location) await LocationManager().start(); @@ -135,7 +132,7 @@ class _MyHomePageState extends State { // start listening to incoming MobilityContext objects mobilitySubscription = - await MobilityFeatures().contextStream.listen(onMobilityContext); + MobilityFeatures().contextStream.listen(onMobilityContext); } Future isLocationAlwaysGranted() async { @@ -186,21 +183,6 @@ class _MyHomePageState extends State { } } - Future requestManageExternalStoragePermission() async { - final result = await Permission.manageExternalStorage.request(); - - if (result == PermissionStatus.granted) { - print('MANAGE_EXTERNAL_STORAGE GRANTED'); - } else { - print('MANAGE_EXTERNAL_STORAGE NOT GRANTED'); - } - } - - /// Called whenever location changes. - void onLocationUpdate(LocationDto dto) { - print(dtoToString(dto)); - } - /// Called whenever mobility context changes. void onMobilityContext(MobilityContext context) { print('Context received: ${context.toJson()}'); @@ -220,8 +202,8 @@ class _MyHomePageState extends State { Widget get featuresOverview { return ListView( children: [ - entry("Stops", "${_mobilityContext.stops.length}", stopIcon), - entry("Moves", "${_mobilityContext.moves.length}", moveIcon), + entry("Stops", "${_mobilityContext.stops?.length}", stopIcon), + entry("Moves", "${_mobilityContext.moves?.length}", moveIcon), entry("Significant Places", "${_mobilityContext.numberOfSignificantPlaces}", placeIcon), entry( @@ -249,8 +231,8 @@ class _MyHomePageState extends State { List get contentNoFeatures { return [ Container( - margin: EdgeInsets.all(25), - child: Text( + margin: const EdgeInsets.all(25), + child: const Text( 'Move around to start generating features', style: TextStyle(fontSize: 20), )) @@ -260,15 +242,15 @@ class _MyHomePageState extends State { List get contentFeaturesReady { return [ Container( - margin: EdgeInsets.all(25), + margin: const EdgeInsets.all(25), child: Column(children: [ - Text( + const Text( 'Statistics for today,', style: TextStyle(fontSize: 20), ), Text( - '${formatDate(_mobilityContext.date)}', - style: TextStyle(fontSize: 20, color: Colors.blue), + formatDate(_mobilityContext.date!), + style: const TextStyle(fontSize: 20, color: Colors.blue), ), ])), Expanded(child: featuresOverview), @@ -277,10 +259,11 @@ class _MyHomePageState extends State { Widget get content { List children; - if (_state == AppState.FEATURES_READY) + if (_state == AppState.FEATURES_READY) { children = contentFeaturesReady; - else + } else { children = contentNoFeatures; + } return Column(children: children); } @@ -294,7 +277,7 @@ class _MyHomePageState extends State { onTap: onTabTapped, currentIndex: _currentIndex, type: BottomNavigationBarType.fixed, - items: [ + items: const [ BottomNavigationBarItem(icon: featuresIcon, label: 'Features'), BottomNavigationBarItem(icon: stopIcon, label: 'Stops'), BottomNavigationBarItem(icon: placeIcon, label: 'Places'), @@ -309,14 +292,16 @@ class _MyHomePageState extends State { List places = []; if (_state == AppState.FEATURES_READY) { - for (var x in _mobilityContext.stops) print(x); - for (var x in _mobilityContext.moves) { + for (var x in _mobilityContext.stops!) { + print(x); + } + for (var x in _mobilityContext.moves!) { print(x); print('${x.stopFrom} --> ${x.stopTo}'); } - stops = _mobilityContext.stops; - moves = _mobilityContext.moves; - places = _mobilityContext.places; + stops = _mobilityContext.stops ?? []; + moves = _mobilityContext.moves ?? []; + places = _mobilityContext.places ?? []; } List pages = [ diff --git a/packages/mobility_features/example/lib/moves_page.dart b/packages/mobility_features/example/lib/moves_page.dart index 01524cc56..ce2cd257f 100644 --- a/packages/mobility_features/example/lib/moves_page.dart +++ b/packages/mobility_features/example/lib/moves_page.dart @@ -3,15 +3,15 @@ part of 'main.dart'; class MovesPage extends StatelessWidget { final List moves; - MovesPage(this.moves); + const MovesPage(this.moves, {super.key}); Widget moveEntry(Move m) => Container( padding: const EdgeInsets.all(2), - margin: EdgeInsets.all(3), + margin: const EdgeInsets.all(3), child: ListTile( leading: Text('Place ${m.stopFrom.placeId} → ${m.stopTo.placeId}'), title: Text('${m.distance?.toInt()} meters'), - trailing: Text('${formatDuration(m.duration)}'), + trailing: Text(formatDuration(m.duration)), )); Widget list() => ListView.builder( diff --git a/packages/mobility_features/example/lib/places_page.dart b/packages/mobility_features/example/lib/places_page.dart index 27b4dacdf..2372756e7 100644 --- a/packages/mobility_features/example/lib/places_page.dart +++ b/packages/mobility_features/example/lib/places_page.dart @@ -3,7 +3,7 @@ part of 'main.dart'; class PlacesPage extends StatelessWidget { final List places; - PlacesPage(this.places); + const PlacesPage(this.places, {super.key}); Widget placeEntry(Place p) { String lat = p.geoLocation!.latitude.toStringAsFixed(4); @@ -11,11 +11,11 @@ class PlacesPage extends StatelessWidget { return Container( padding: const EdgeInsets.all(2), - margin: EdgeInsets.all(3), + margin: const EdgeInsets.all(3), child: ListTile( leading: Text('Place ID ${p.id}'), title: Text('$lat, $lon'), - trailing: Text('${formatDuration(p.duration)}'), + trailing: Text(formatDuration(p.duration)), )); } diff --git a/packages/mobility_features/example/lib/stops_page.dart b/packages/mobility_features/example/lib/stops_page.dart index 791a83822..8724e8ae7 100644 --- a/packages/mobility_features/example/lib/stops_page.dart +++ b/packages/mobility_features/example/lib/stops_page.dart @@ -3,17 +3,17 @@ part of 'main.dart'; class StopsPage extends StatelessWidget { final List stops; - StopsPage(this.stops); + const StopsPage(this.stops, {super.key}); Widget stopEntry(Stop s) { String lat = s.geoLocation.latitude.toStringAsFixed(4); String lon = s.geoLocation.longitude.toStringAsFixed(4); return Container( padding: const EdgeInsets.all(2), - margin: EdgeInsets.all(3), + margin: const EdgeInsets.all(3), child: ListTile( leading: Text('Place ${s.placeId}'), - title: Text('${interval(s.arrival, s.departure)}'), + title: Text(interval(s.arrival, s.departure)), trailing: Text('$lat, $lon'), )); } diff --git a/packages/mobility_features/example/pubspec.yaml b/packages/mobility_features/example/pubspec.yaml index 784df1cb8..d83ac94d1 100644 --- a/packages/mobility_features/example/pubspec.yaml +++ b/packages/mobility_features/example/pubspec.yaml @@ -1,7 +1,6 @@ name: mobility_features_example description: An example app for the Mobility Features package. publish_to: "none" -version: 5.0.0 environment: sdk: ">=3.2.0 <4.0.0" @@ -10,14 +9,11 @@ environment: dependencies: flutter: sdk: flutter - permission_handler: ^11.3.1 + carp_background_location: ^4.0.0 mobility_features: path: ../ - carp_background_location: - ^4.0.0 - # path: ../../carp_background_location dev_dependencies: diff --git a/packages/mobility_features/images/Untitled.drawio b/packages/mobility_features/images/Untitled.drawio deleted file mode 100644 index b26c799ab..000000000 --- a/packages/mobility_features/images/Untitled.drawio +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/packages/mobility_features/images/app.jpeg b/packages/mobility_features/images/app.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..64550c5fc20af55a08e1f0ab51d940d122efcd37 GIT binary patch literal 439116 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�__XR=!_JNSPE71-25ycD=kT&(xO<${ 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

9&Dxx;u#BGN_SLFp3my# zr-{5;-53p53<^O`IkKK^=7okkTb~J2K?9b)%@ajB8osEc_Xbxe66q(SMleWss0l{H zAB@I6hykrMyj`@WW8QFIZ9qv-J>!_{COOC0C5R0#k$~w`Njg(rNtJj$!l{PKWAVD& ziAvuELk~v-&MvtaQ93YRoSBSDHqAG%#<>-!@|Y&OG^S}ZMuU*fcD5m*K^?xU#s{`@ zGJ#7G^uSa*R)C9k&W+F;+Cg;LNX zDQW~`9f`SD`n%0^8~%#B%V%$q%r~YC@~@^()pivsWn*fn4&D{a=jIAECTd5Z!s?>; zQ^#Pe3upb59-E~<1Jzw*JDPyy%ZBKOmG@QGID=ORvs2-GS4(}n=?NW)8itDB3ha!R z85B7gWBRZt7w36zs3+3ljAC((8&!n0=vxkwSgoxsZ%ju?@Motz7aZ#mMBQTrV4)xK zsE4%nRe$Z7h~Tsd%xCs910BU9{mq)~*5#hBoFm2t+=~kz%A6uOUPlFmnrst1Olgn0 zmcP5LOLdg=7K21be4vDb#cc~aei$=rMs1kyQ>E-YtE(X|ol;?BHTI&8n11q|83EHq z%)vF6yqD^AG`1mQYL7GdF~(cKHEO393VjJrdZHrqOvy*rTdZPdIS4N}@zp@5f_VFu z>tA!0NMFViPb6}!7rJz{=&5C$_7K<#K0jJt-R7-PDoFQs!#3S)q*+P`d$KONY=z>A zokUF=1`r^;J;vulRJFCzGTz9Il2smljlyQ{r8J`I_KcBuK6yk=kNL|gt4Fu!Akq(2 zgBC^Rc8pof#Gwvs!+R@s{A((=Bc1H>oGz?XSAvfs&%w3R_avxs;Q=&vIQXNgJU|pL zgm!Cs3ibq7xcn-%S*$5EYZbNoFNQXn>Cxv&JoV`luu+?N!}{Wl8I##`_EXp^!d@ub zvMM>;O@c8M$ge)Ke{5(7PE80%usXp=Az#y?+ExcS#hm|yV91PA>?F3tPTdU$`)7#8^1NV^pq_;PHd8`9yruxR*TSMWqH)5%1O9XEp;xmkfZ-hU>{Dn zXPfu361HSp^Kcpdtsd~wd?76JMlLJCY2kZsl9m;V%h4w8ENM%WVcqUpAHIogcJp*k z8TTHrS1~BF-YOB#H2vD1e*$a&fIK;l^bQ|zY#arwd<>E4w&A0}h$YvM<}wfg+~em> z&WxeDk@+w|<$+-+_YEixK3D#NEHh_w@tp)`k})Tr2R#9bh7M0*Z!}U{ovd|NaK>n> zWlp5a)L$wkO%s0>5Y_U8%&H_Ftf-|{L7`UN4Iwk^TAz3Klsq+*;22oMJJ2|j#5iL& zpk(BI$v4w3_3UVRO_GpAdzb2)EY{of2|0K4@6qbkcd3@v>1f#i2lq;AklJE9Ymxrrt^M>!EJ@?8NLc&%?`r3LS#$|BV>JxkMTjsuacdy`UU zn`h?b47(C_$+vR(WhzuHg(x1 z5=T^+c}>`*^!6((I>mT5v+y5{-{7qjkk5Dg04h-NTqx-2?5jz(P*+d6vw?;k*Co$% zl<-swRVGUBP0tCX)=0ZBhTh$<8Xy*)S)EBXpKV)Ef6a|JBGee4sz7{vv30D6tJ(1$ z+;c3Nqq;j5o1cO~#?&n6ax>ZK>JW?SlN+?J9b-@Ktb&@yu|&`)sHA%7DZ|%OZjNw{ z7!?T`BnP%MJKu{uU!dFil3F1pKpUwt(6E&_Q?7BsqJ?Cr=ku-2sj~3Y-{djaL693elZ;!e>yM^J)S$QOf!+m2knle zR;+@Fa*5dy6a{yxWo9GNq8Cbv$d{e{S6t#R_X-H+y@9Okl}n3I^9)MWqGmn9q4DpW zu#Kmd!rd-v>y8{(eJ_ompRuA-iW_&y3P;@{*|t}@A-sCFfpakvG3;d2kLZDD9Pg69 zpf`4fQbcNvZ9157OREDvs1WRfk`h(5B0kCsvhTvU(wR@*Y(K2bBBY*SwVuYOrGLU( z+2Bz_WrPGpO0kKoWJH$fqMk0Wrj$sr{`I*!oO3UmR{8aKdYbCDtMXLav$$w+ zcQ?@|?z}tWv8$r1l_U(dZP)X{t=?H0ON66?5Jp&%lImbO_l_K<1YXfv9wwI?YvPSg z@-spv*zYUoh;^maChL}r6|Y%^oRqifj>Hq%eYJbbrAz27L2q*)@4Qq2$Cg5u_i;Wq z6xr&{aX0@c;uU?K1QdPKf<0v3(I|*x*8_&#cc3^X@u}PbXOunq3Tnesspg57L^{ZG zyfBM-g$rkeIFBx6zq#Ucux-3@3T#cV>N7gM=I(FU`v^nvW%dc^C>@7x! z$?RIV-VQ$S)*ns~)Kw^&T^2IoEd*9GGqs3l>v)RF0V-NkgXAQz+T%ojUNuh^9e+YL zkiBjC?DSKWrlSMiHVIe#Pg=)ukxo86ZdWgB(ZVZ(Q*s(tYO35Kwl3^Wbv!P-9n^wq z!DIl=ppViQ@6SJ@iliCZNZK|nw<7_{-FNDggBa=zq0xB0#v$Wv9aQdR)4BuN*^1-B zH3;RZsu^$&Aw5UU zf77CJN#L#y$oA>@a7J2aM*Ul)!~mzFm_=;* z7w!N0CzuY|2U(R=Mpmhg?4X6zI9HdiGQne!Gb%eqs!SHKk!1YpDwI4vpU;%Sct8pIS3|oZG z}vsZzn_xqhCXJ1>*Jo8g>y)OmvQgd%@%P$*}V+g^ehYDaK_ zXENA_xu^a_3+j~+-{@o$ozgOMy3CK*lKPB>!_*LWwG;jdxQMT#5jW%kyD#6>4o;s_ zzBH@ZzE?Nm%5JHtSCZhDJOa?np=f;1&vS!xBE+4Tp~wU$O)QRmb0|K^%PJ$?@~fE={e=bEr>v3tvSbYjcV!J(=o@HSH381fAtk0BId>-HB% zY{zktzeGIOqbD9HuC5aW+aGyziEVH8exXuatOlwQE3RB^{IPm{$#nHbp;l)b=Lmi7 zQ)RcJMIjT3yy;lE>IpJq?(#&G`!x6^IvqZlvqIXB#h826frU#7M0$!cfbj(k@6q01 zrA4Upp})v#bRu_?b`v$hx-$hq2FqNZx=wa5cT1gVnL)DDHppJSWuh~t;}68w zXz%`DlQ{}}c~>4t{N95}>~_cQNMSjb0U;9D7!b(S3i<#NCfGju)Boe6nEfaw_N!~= zw~&wQ|9;DS7`itput{YfQqp_>n4SC$fd7K@I`%Znp;WBRvG&AXEv^`vWW>hGk6gS*=!aMC;7HW4&AGI1^ z7r~FeJh+}TEb>z|F>KY_>qzxo zvMsc|c)zBwBap$=rdR+`0K?OkPNwr}a;IJH`5krOI!nb#@ewxujz#DBNV7&rV;mRi z`F3Ze0eii)XhzxB09`CGztHnMl)=efB`f3DS$k#D3riIK7X5Br;QLYWhZ2fY2Rg(!?0Kf+^J3c(kf3G*s0K4k4bmcnzzolZ5nb%2|xUO zXQLLju{$5$+f;KcrIY}B@3*_{&RQAvqZ|9Ir6A~W`^+G~S|oN;;`ub)j9C#q=fk$5 zBvDBKQ+swCWBXKq99vpeknVUeQ+VafuutGlY^zI`gn{Ex;&V^uxL2&CU4y~?cn(`h zFC^28))pgC6;VZ+@KdQBI!xuqMCET-N(ylR5P>M#QBcD2C^9%JuVJ*Ud9xgnPKW2l zUgIZiKLxf=!9V(FfN8*t2OPo!ie+L@$B{E`iy-Ep<4ItcoO0GlJfi<(aANv#0fK=5 zm*a+;`%Yvynd`g9l+z&(*p#yj6fQ{iAM4sndTJAY6!okxeL7h-MiPVg8Zij{*fP*1 z^;ghl4uvp@v34YI=F|uj_rk^}_7Wc#py-QElz;gx#cq3N#3!&ZK&1dMwRlh>{Gxsm zPjVW>DBR1)XV<%9G5ntXb~~tUsI`yktlw*nW9eB}qcl?$k>`L59K zLUyuAm`V*f)5$o@PIx&mvW^{zRqS@@5~fZm!sYHgdJ{|2u>?33-u!#=KRMcvcrxVc)8P^FC zbbKC$?vtFA5x@3F{?7Lhr6$fB_65WHz|G*M*I1NSb)ftR<1w;V(|#9T(_x8lT^TkO zfF|MvoUNFZR>^iLN@kuS?xn*Lzf|$)BG%weC!zYtB<@pXFE(DPm3TC#cl7{U0SBFG zs*y~QS_50wiFCqP?w|LTJZ&^F?2_gFB1@lyi;B~s6#lRsQ5M`a`~Yjs$>>;x(=Z)6 zrBd>8<+dLrnoQIg9vcPtah-nB+JIXmKu4>{%~Z-Z9%SU{&f7w-CV(y&jAqegIA=uB zccR+d*16?wA{Ds#f?x`=J6DKW(<>CsfqDywpP~lN_jdyx9k~`7$OS?1| zds=lTGEZ^qdmX94^hbHkYp$>4Sqz?=&=U-9Pd0m}aBSvW@ClFI42cP$2(qWKBjOdv ztFV(yQH9R==e4}qmv*`w$Zl1z3FRO4GioC5?0I8=hff_TSrg!`&*>t|b5dx%1trCY z{{7oX^QCvLVrOPz6Qw+tI3x)kI$w{+2hA3=#$i8?r_^Kau%yFnz!p174d8<^DniQu znSb^!I?bbr)r3CUeaBcK2TIhk5qGSq0u;!WN7)w_f@&==aSLQxfuBg6)B?EL_(Bh? zG8PB#8aGHsw_k=vGn`fFSGd^i&r~rbh1yOEGVr*0 z?o7Nh^Z_(W{cZh?^>+a5Y@I^W7 z80hji6m~UsC3O#I^RGG*TPVko&A>q>f+K@%km?;TR^;>7s7L~cZ~1%5Peq_r$*>*- zY8zrt9?9Tqw1u+(q_fu+5p8>ZfTje?yLy+sd=&zOA_Hc6p^7q|8wBsak>dkWWR)?7h|t*v z5AKU6&`QF-c>ZGCc*FLEde3hOI%&5F?ot{~iu|N~Q9O#23K2ObWv;hRkM1uCkKrIe9AK-6r;rEpo zDa~lttp17Il;>K20nB-`w&;f#lgY2Q__^drTw3A^{Um8K@I%e!cKjgpfBiWB(Ofcv zyVT+iDE-&7qS!(?h6wv;H}=IJ7%-`Q@rQg3;|IlGq6m5uI65!Z{Qomy{_i0FHe%S> zO=h(?2Rhft6jmasW+1bl0>;?Tsw7z39vu9UpEv=zBe1QZ`bxcr^}N$9oO4o{id|+{ z{#2;hTp`z(wMmxmcB8*!vj`sT=t&OP*Z}s3dd}+pV8bRx6Qn?XFAyJy@P-s3U1==9 zS<8D*1owTk2FUugjFHVoAj~8ZyNOo-9BN3NLA41i2?{U-n8n662J#6f%GQM+t;%<% zbX4*NKOSc0^W1yLg@d58M^tCl$36z1;J*!p;nWg!chig&y)!ouAAZ2FR*Vt4&h;8S zW69BvNHPZ`dEd5KvB%%~4w-+pf1Wuk3pfYs!Hd7rl1tOuNf!x&!0MWVaN zmKrF)i#gf~AX^@M0Ff#`NHti#nxz>(inj24?{{g}veM4CfL+qr&}uua$4ymi?|B-q+#Z1-y^DzvEBsdmplgc>IGM z1IElP1N!J|eGWQkuIr1+a~VH6MxO+L47^+QGruS-PrDWo_2{F4H;mnP3b+BW`GqP| zz6B=cGrt3Fu-SJEzDJ}J4^FS3(H7%p|L~*BM)%u}9x&1$3;l|If8!ngQB(h+Ne$cg zb$;Qt9CVTo>xCQ`hToRbeXsxD@ACfpTlgcV`u8Ijhjc*yH8V-KTHgO%=FZ{^ovHYK zYy}~Whi(?1-8KN{>S`!OZQr(!>MsU5@y zb^3mr*|nB2_!Re8@E78K3g!P3pSI;w@^;}J(~MRa`)S0<u*S>9tXCkGrpAOP_(Q>OO5XCYv0QLS1B zi{&Mktz$&2N~BOR)@ryeXhr_+ZH5l5R~+wc^^dB?&`VnwUt1T9f9}QTk2nLQGC|*a z?cG54bg&<#|@e63&m1a3M5hbJ+)EFK~0OlMLL(i#d*w!j+GnnYu=< z<{2%^sgE`=t!d9O8Ow-xCK0N(l$CiIu*zZ+FI6anW;&j0xmjDO{Df0O1vh>rg`Ecw%x{;$dR ze+T@K%cd|1=e`ZUC&Av%Cx2Sabzt2G#mGWArQjvo8k#iU zig#R$90Ymj^A6^sKHWJfqj zn6O`#1YSQLTU#)M1({E~c?8;ab{o2pR!oOdw{sEji7z^0oFO_yd zZ_s{z+7ch2^FR}E*AH3#5zwiBru_ax9kM*`{s@2RKWZ;IjzC--N7X zJ@AA=2-j-hAh|D3<@a$aME9pc%>o$x{f9hpxC&q&h2Wp4;FA;`s)F$YkKlmn`PXxS z{~-z)6Ts~6KPLE#h<$vnZ$-@Y&=veZMh{=X;d>ylFU@~F0?x0b9QA7{|4L0*4qHLZ zzVdxX&kkL|N9{di1^YVjm!hKlLeZRmp=d`BS^ks$f2-(+E&pj`4^jS;M*Ia70>9KW zoS$pj>jM?M8o};6K}RE0V#CVs#4rpXRj~w9@A34EaDEjZH;3HmsAuF!mKElBs`~$~-Am`t(m9KF8dvxajivCFjKj%w_=)un` z_#Yx%e-WmyRQ<4o?XU8mh{<0#{;Tp|pkklreuRpHn*J@>I4I@+5Rs3f^DPv9W9q*e zUzQtE2|^e0-rqna6P|!^B`OLdQ|(%0gmYAXXc6|U!k!HR$h%LzvW-W~%Oj{Cmg@jp z1mP>d+Q;(@myeEO+eF?(bRCgK+hiXYsL( z@6f(qDeGb3;*SyUPkfvIDP{jdyrTRCY5T^z`VTAXm-heEJNri2586b;0m}A?7aWw* zLwZ-g;T(Ox2cO3Ed(7k)J@^){KH}0}M)f=1)j^y12NfKM036=C`Z%)RQS@K+;9mf_ zzX(EngV|phRhECdd-it%{jL}8+_Q^TklI`@>vIJkG4+#EbC9ilha>!|f`jn+5A6Ach5cCVcj7Al@CbfU!C!%y zulV9WD)`v5kA(B*EaMOr?CZogk)0nY+V#&Q;YW)42S)mUqJ1gToN4{Ac^LhERpyJOiIJ^&>LHUziad9}s zMn720!^n%=b-H5IZH3s}iNtU?3D5-Wd&KxR6vg;FFP+3>rF;J5zd!%kabT%NrkiN^ zf;Vm}DFLZCe9#YG|HfmjB0qpE${t2vffkL33XuSZ>Y>gEyNVyhS1z)OtB611MfcyI z|NJSi}2v zMWKzBz`dnT62SI`_{_db-(7+CJs!{CJ~FSllC~jYzL5@BINs|8Bnuqhv(i;O_pLPofL1V=&P4j*h{$J6U&&>*%L>A}>rt`e2K52P_$3Ulj!AoXKtf z&Eu+LsPpOUWyS4)uzn7|M0!I@DvG1BE%=fIYE}*k4bwXZAXAzGWDx< zR}l=v(^|nrT3}~7?5mRZ*&pCi?{>;l(4!JRW=KX4v5VtXcpxy#->_w;&sSeaKy^}C zv*)uO6L5-SRukQG*K0@F8dYfQICm5zc~9c+0Hb}(LuMPRCd$S^e_povaziex zM1VIlbWamDe{}^o*fv6t+_)cWINP#7WcN)0rjV+C5?jxMz4^Em+PcRZey5>TB;!Nu zPUt&}ZhbVCx$PmnNQeW#k;uW}8ChParYj0fMi` zwUw)FaB~p8*SdHIe6m?}((M^d?1_tZ*}6t?z*mRaIfN|ZO(p$J{g!vvUj%AxTb{ej(_rf=u(FWl zb5wfom+U|^Uf&!0>H2h{7oRsztVw~h0*>OcLYRSiG=3YCk6ZIw3BB8_jnDEYm~vi* zy^uTx##*jLY4W-6FJ1{=zKGfx=_`|BI$*FpgluRL2uY}t_0)5$&Q~ca&30LkX^1mi zn7rjOB{q$XYd~B{waVESIGV$ig+byNHXT{+XOMjZqf@ROff zxd^<+zAC$rJr=%KR8Jl~GA5mRZd=dsNX-m^szVeO_uaKO1#|PUPbY#lQYQQx&KyUE zVO5?fg<1oh5w`?%Rz1+c_EcVC(R|*mCpNpHY&sh^3{F?Jobh>ni*Uv`EGWi8&WAWU zr^pUccFFlF1_L=}C}SIb#Q>3Gxed=mx&(QrU*7Wuisxr;V2cS%KJ2EVe#$#lj6B^1 zAjLh0#=vW=XNdbHl=!BjPIE9yPD~BC`W7v!#Ox3ixM>o%)Zjb~<|S@RSNEi}Zv(Ew z2(A=u#MuC*egDLWYq+sIr#rE#tVV`|S|zXP6*mNUu6TR#r%VP}MYj;W@wzmfre&hK z8HiJOz1Ey+)oT*f3pIp@yNP|#$BW&`Lh*R0xSD5p*m6+M0RDnDI3(fZrCgLs9qDB2 z6itoB_xn}(hbzgw|1 z&?*?hZyQl44z(#vosYc3G;HG^6#oL2XH9VWr6Qpnu?V`{^sJEOvvFHdCi4;-XP#7= zYxobf@DaB!4XETtClK~zYc*MTh#YUZ<}G0!R$?)o-Jmr(3QS`7B?$dfhtAX|)3K3a z3^+pKSb{D&r~J|7O-za`R;{;EQTi>#E&9Wyc^kF`^Dr2&hIKgZ3KYjRf}5J7)8`mW zS6zKPJhM{t{UzT7)ihwp!T`v`!IdweC1dO1>}D*XlTz${&PY94^Ax28bM?`c7vg=2 zZWe%^UdEx)W%IK)$fd@bYK)%osF>g!%CU1e%M;XD{#u=dO^p3=>wJ(uAjEVMY!4?m zAH8kBOyxuD+=}<#zCE4eFK@Q@mUu19_ObsyO5-spKiF-Fb25&5Y?)RwrWwau zP?|iOGE%fzEtbU_NrG;1hcq_MaGf}&8_YUMS^Ca)>`_8sjkD7XIWTyIrFMa0d(u4X zExe*1K$H+n7dPkDj>}hX36Q_jSA7-KTzYM`LIAmR5W^mtNo zjyXk34xPD-SiT9aLY8q-x^|_BwB_c~_v{>Qg|AfLVRVGG?Q-5MeK_A8L2XJ{RgdD! z2FQ|PS6#^}g$vK3TtcYuv;=8}5QT#h@HhbPI?~YYiO}c@A&1O$C&cOgD4_{_j`8o= zwR{^dnZOYGWxsoCU^LZm=6=d}7<*20 z)#M^oN9pORSW;b>+Qa<&MIJA?5Tt!ew7KCa(Why34Ja<61jOX`JMzIsVHrL&E&ba^ zC|mE!$w+%X*b?O=eLRW>MIBGhUNxkp(&tK7 zVERiS;xJFLV^y)EK%Sy^ojTCJxHlecBDy;L%In^=Iu5dR+H{4y^4yX~)$zl%e7QFZ zP7T_ma$S&XlgtMjz4E2?1J0PF2JQiv5_@KWpJR8*u!X{sm1n+1Rx}Hpi5#YKu1HPN zd|c4L@=T+SYR2ulGTOowjE82{&F&ls--{t3l4u@eCBXbHXtEKk6`@)A8lSeh8RbzA z*4U1%hvuOPo`h>+)LhICyQ(cm$9%5PB{_ohO@%4%Xh6(u8fS=YwTLf;Xe}i{*M;?Y zm-1OW@$6c>tj1gVeG=zgrX=27zcsS)lxQ~wS)9BOEjLpk!c(ucnPFH(N#D+Io_G!Z zM5@$l?wspZ-I!;~`gn4WHJqRC+0b17Q?Dn4n?g0rl1f4(8~Y|YDg3gFG&XF@g#sRa+YZ|M97uMO=f*w=c=uPdUp4MDOWmI&%(v_ zmo09@=>7&rBpgXmL&#h_wX(K@l7`T`^D zjxt#xZe3Bortdxm($si)jF5Bl2)tn7Y@`E@bxG4H({#hgR}JpdsLDPbW|gJemVO=R znn8j8UV^@j5P)G^Bh3+_&=*v^6i%tN57n`~IIgPhyw%_3ef`J|%6yz;JcFwu5ao<_ zD|G{ny&eGmt~7MiAXCn_=Ws_L((W>J3fa*3GfNwoCaN!nwZgKw?ML^mAu9TfCtFd8 zoGqkRHjO!^0|H#@1oIkcrNX#j5wcM0uz(iTSf$RvC-+Od_%fPm48ij00F!VqV^^DM z+L%rd$6770L53*NT{I0;5j4-dwS$?<*aT9(RONsUU=lT^vLKW)Jmm>)n||l4M2OFG z5sJn!PuXs*{@Pa!D`KgY(^dC#j$Ay#2Mi@?)B6R<)(RehwJRf8OHuCTC9Xm6j>}18 zH?g6%auO3A$;#~K=RMXg?pOdl$AS)JZ0;r}UFyULueO}P>I9CPl6!cZN}sV{op)P8 zQE}o(Z5UZwZgaFsD|F1= zfE>pj=mtIcOB;)<-8Ds$(F-{OwXf^(-#w;4sve8)PM03XuX0|LfKdixFfbm!9B9Xe zfK|51c9mQU&N(Hr(Uxzhb-ixPprGrlGR{u5zny?!2>Gt*qJK5XD|X3ZwzbMRBkZe2 z@nPE^K(6+Y0m3)rBp^1kSDDR5vyJiWh?<4CGwwRg8IgACHCr*HNnSid3#JFm^7DIm z>ua755>2ipkI~y*U5OH-c zb`nrVnc~`#%MU_tfnWg-lpm}WEEf-Sr&d~djkch+&OO#w6iVr1huSSsSSm=~nrB)OiEc?xQpU&g`Trlj6#; zL|iR18n?wwF0G?QAZhN#y=xn(Z6TzKelvn~z*OE+-wGyaNTi3T4ER|op(z!ws*~z5 zaOgf90YuYPRf!jsizT#HQ0XG3mm;RvpAq&7>N7E)#VzHI4=pjHVS&< zP8dXHHto^0rJly54m*GSiAf z!zB=B7&3_JZ_&dE&!7rDsnL*=5XtHflP@kRFqE8%iR>*qbC!;BN)U z{=DR|%TK~?;0Or34=z=@!@oQxShl`)F^DMIGJ$=s6jq<^I0QQ};r$Bdaad){$*ibr z@6r>5ttc}3DDcEiCWZx~Gyvp$-E0P817^Y6*i5y2wpRdQ%RB}*ggIP+-AWaVNWa#Y zaGP+I)A>p>L&KEtu!vzcc~9;@e9cWFHny`;#+T@IqyZ2*s?ENte`9^JONN1+#B(Rh zVmZ=Nzbc?Pnf~}0n(`;f(i4lNc4ONcGb?$urb|@vkeRy|DF*IhXaxaJUwq`6P~gcs zW>H0)fo8IFQzgKz5nTpo$4zD8C;rI~v@*RNjT3G8#DlaW_#25p;>75DC2K&rEG)7b zTCHtE0ehIFBuCAYEovpHOmsL0EW(*lCPi}_%WOrgv~J3VS<{@)>GsIkyKk}=cOQU5 zP?{dSD-0-H3i5J{zs06lG0}{xPt2QYPA3C3{9w2RY5ihgWGFt!iLZ&3ZSEGPqx!ZPlhDeshpbO ztDm~O#xHWwN6JnPdp}4gtf3pG8&d?#F5dC$_(7=ZtN#_zvb^#}W1uwohBOxRIYvEkmv0ib8f)@w2lrz8BOH2TJ~ z2f5?ad`>2+WYp$t$Aw1#{Caq7#!QWPr^9>QEI!Cj+NWKnC428uM^=c6$g@^wecvwN z0U8;-D<5o($DbBYHYmng;7PEZW+Lr-WldpXOs)68z#wZLFvTbDX>FWpLmAbfuzE5}({v zR#K0?6paiY4b92othzBevPxs-KzHC0=#C-Fpbgz&|0 z5}dggG%f!MFvA}szcM&G@Aga(3Z97l7_w_GV9@>w?|*pv)7bG}-}`%+@BL0s@(ee( zi9H*urQSqix>T2Ifs&-lH@sw-@;Dj~Sr&ZajFlH?rTj58-@NJr$kV1;w7k?jKYD~g zm?^yXt}tB!W+mKd_om}HSnDf(7IP@IO%1(5cjp)Mz#mFAc8eq{F=J2OQxa@ljF^>S}0B# zdi~fUWz{&+=z^PU`{fSr$LPGHzF3ZxyIAn%8d4T_YcRI)b;@_=J2Uc2N*-sNM3?Na zJE_~%H~=166auF`y6k!4N~Bn!UrU$=24!i_8{pDvBt(8?SEsX-e#{3$p@${Yz$n3i zPOM5q=wy;Cxr1oZ5jar9jZh2k|NBxwBd7TT$E=s|!b8xA&VcT1+ z4wEH$MLOJQYYI3_Yas$Ywk!*#6S0(1W3!wCLwFmD85-Qi&Tj^Y?ihF%-f73^+L@`B zxxzBeM2Qh=IV7u#`67ov@U|(Kl?y`zS1pl z{{3~yf+aJGQ#JgX^~1C_7?}jX>@5;JbEI<6#_s0jl0P+%((*bnqw!X7joST&C+K(3 z8!O^)k__)_THjOwzCsYFx8uP5t&PlRh+a7nykl7`sCuja z${=?jZ^RO{YbfhH>0V-+)$H{tqWTunwF&0_N5_27@FFV4z`WFkF?6VN z^xSp>{O*y()qKPx>idn{vYWzKaDgOIpRE0iBed>%iN1mtf`I&$a zCl_$GUMWddF@t?Mt^@n1PXtw1>Z5qXf_CS$_m2FQj#*u!6mt@ms#`2iqqDqo&b8XrvgmAX!Ix&p2Jme&4Bu&~- zmQw2t?}BMe(2#4L7|7|qfkIT9>;xIf2{4^~?t|YjU}j2M|#@r9td> z9;vo|k{xF0FPCR~1v{?AvfMZ;dRIQtS-|<-_4B~x({<3}E$2h* zd+fzi%`MOy!kIeIh_8ZcoKjFXA@l6bA!x0mObRZ)t7PQN4s#qvhna;qjFsb7pYa;T62t4u7%(bA6&A1_0Hys(|wh*zT8(@Z?@KAzB#TZ&J%}YRaWkxNm6r+RMWZ{ zJG@QKq359^7>*c&dXr`pC&E?7G7Q>TGB|g$V2zZ`Pn;>lS1zK=@oIZo{}zEcCXfydV%Dz7d#w=7{}x zaT^x0tFQpcn74vcV~%ZNe|yB!=w{@Q>BNasZ8W`dz(W zgy;B;j`qAMCvp}LUa7B*p?JFy%{`|L(JIZ)D~zGmxo|6%n(+Ra8z`;3nfH45#q{;# zv~KqYd;kd*D7>j~Ej{g;??>VE9yQ)jC8_}eB`e2tava(f+_MRq2uVt{Gk-o}%pGaE z*b<2-I7w^`vTZObc^K=C+N*l345$>q z=b;l%dJFqx5~6PMX)bS9Rx}dNF6)R2Wz+Wcom;IbDYWp`PXsyUufPb+$>Gl?&Yeqw zn~~!_+{!!yn?m{}8eghQHXhfk>we~aG5E#s^)Z+7Qu2FSndSv>nU^|RwmBE14R*O- zhRqYUG*z@7(|YQ#nF(6yse+Kh)kfDxj@&!Jdp4RXl+Dfp-w!oniUTXN&|=^zB^_!S zEV*WxOvCZS+oko3=8MKH?hbn$-g*H-RwX+&nNpo)$Kc7*r5*gjNpP>#X?^zBH7w^| z9KCKo^Ryc~ZRNBU45KprV$~~;E>x|Kmhd$c>qk$l$y~^63}T+DdgP6el!~SsM>6@J z;1jrHU>JJa@Uk`ksdV4xF8nj&7i+Y2RN?7KwnvtBZVS*Q%-e9@sqK(_%y0xsC`s-BT5&E&prQ5IIPD!>&%QcxVZWkmCofM)siiCu-Zs4QL#axV88mYFA9vA2D73ynC z1@*LzXbc&s42V@ee<<7Lmwa=ms)7MIFynvs#K)%MG~J5%k^ zby;hlsM?%1rByMWIeFI3f~(o)4vGtC+~pz?gZP3oo#W-~0w4W{{W73u634@Pqw_c5 zS`5)sTOdoP`HHfmj+aVSC_+;+Z;zCA=ewTPUKm7S4Lgyqene1-aoB&5b>yz_P?HOr zhXBe}97ZQaH`ib_RWj#}K?GxcI>-*EE|zp~PY`aCs@~*Qw1&K=82GPPx*95 z;L1f7c=F3Vi08OL3|099tx_+>@I^xF)rR0B6V40XR@YN^6{}C$Uk|gLxzpbIrd7TH zB!yeE=&Z9Yg?|dvR5!Fk@Wv~`|fDA|F`d; zXzjgMjnZ1Rw@|gKR%@>swO6emcI{2k(u&q>jkJg@Hbqr!rA88?Xh_T;^vU;k-_JSs zGw$DC&w2iEIC4&s&v<{{*LA&?DrMU~YQhZkb;NJ(sCwYiUj8rOw00K88UFAT^ZPI< zPyScho|2G0mundFNj)S>RSucn?LAOz@!nhL1X9?*@KBJQ%<{Ir8Yuu=6cmNZ&iV^r zkaMKmu{_#Qrpa5lxez*yG;X&2YB|0*2(Gl^N?Rag!>YPtYLRNtfi;F*nOzQxFZZ-u zf>bLvoS8<2zgD38e1hhid)!8OA7dpmjw5$a#b{XOgC|xO=aD&736nO%xMe{Zd^isD zuvUBphOg4g*<85aAlnnl?A-Tbe#BBsp@+En!#W zilTmsgCP$%waV(DaeplA4W3Yumu$Mc-~x|iwbj2PAByl#jON67#nA7{hu&E1D`z%>l6)yhRNV!2Bd8LE~6@Y8D+;d!AmR>+15aaecr#al z*6*|`k2MlT@QrG;OJx53b^Cpy!^v}3LrdKx?Vj(m7d%$-gY zE&UqU85id%1VhgZsAblcYsnh1dHm96XWRA=Ag_!{;2vnFt2uAkj#hvA7vKsxFd?ll z+ek#~DLE(9Kq2tkcmwQN63oTF%%e5pt@WO-j~j0=Px-F}U(CUf&04B27#*qCHB+Ug zJ$OeFLH_=%j3$2w&X?Tpz%`3E=vDd>w{^QIgKyH$8yRy07lY&-$uzVe47_SrJUU)H zLOZuiCs`&ApC~$0T(~eFSEB)~w3u3}mS&#dJeyw<8=M6%x^1~@ORrv6Cq{GF*o060 zGV=AOFx;dIy`SN#@c!ZlIRs3JD@VRQ2#FyEoh~aeV}8qI7j$p{?|OI!wz6Q2q9#j1;q-qUwt=M-Hl! zW94Rx8{%U8gtCxZ_WQ!cMxQ>&O|o%&|L&m&n|DsB^IL)V7ORVDzTZwVE)m|O3W|$c z=mxW54Iks;kie`6p4!?`CbOI3uWg%s@;;rnQ(1D8h09&2MWG-xp2}4vAH0hT%DojH zoAEXBsa#x5YXu9GmC;h*c8~{YXB1141C+M|19Hh<_^D5}KJlf(i!pqKYS%Y$cs z0k<}1yPgk6jw@EaaLpOd6Uwg*TBNuT7=H8@fJt8!4fivKGhT(L$ z_aE_M*jG@s?w6rxZ`r&zJo>`tDSWO}&pRf6T3TdZa@x;qhj^Fx>gfGsosUaKL7KJf}V+#2Xql=O}Ij?^)c&Zoic1GP+7D3V>JN$_*7uZF@x?HvV>jV6(yy zEwBSu!2NVXymOL&J9>L%m7jZh5<|ZN)u5{>nrZp4E_{5rMEjYgoRitpF#Z=F! zl<_Q;d~0XExk~`)pH^h^i+r$HRlcyXZPYMMm>b8u?Z~9ciHc#yYfpC>AwI6_L5y2T zD|DW9y&Kt&SD9DB77ZRGIx_w_cI24!k8($P&T|4A2Q?kVHKT^Gd&4psye_+Dliu__ zFwPP``E8Ow=<5>*FZlTsqtzUzcIuxx=OZo7hOu_l5NLJXDpHFI&3}Dy{}PzK#lGXH z4i3W3offyKll(9+tl1+o^t}Im@VQ1OB_NFGD8hsotcF`qZ%0r<*+6M4PO#>;wqG^f z2NOvEWYQ5Q%-i|kAY67}aAyy!5oNh;!f2%0lmFg7kgOK9cu51zSMsV#RAEZwn6=MW-1H z^HN}D&;%fuM;teSR5Kn{PL5S)x4u*UaYy>KAB*mNQXfCI=l!KbzrDQ?VMDbOE`#PE z(V-vWE$vLk3a=_FIGGZ^<9+u^WgIwbTQlT%Ga`mY`YzK!tD;5MGeyom>&Q-#%}zaQ>0y?r&rYq3H>Y?AH@*;6Tm-ic@B~d704@{VEM&rSCHH&f(i`?V z9Ft_X=b>5SA?pZ3@Wf2qgy(Cz6(jy~p|8X2a1`$|eYMKEUhS~U5dlGxwH*!KJ0SAY zw>&Di%JB8b$fuNEKl}vs?h7qXev?}5@K5PsKS?*{(83(`&U=T~|I#_&^08s&ze8Y< zb_sa&ny(cj@}r8-jFAA1RIwJXH3=Gu$|zA}ru)F^Em+$E%&-&WXR;;!{nN4?nuJOx zyo^>D=Ys9nw#62l4fwMyRaU1o4<_j&%%<DP^BRp?!wn>o+-=MS(4IhiBO3Yu19*LIOFJ zM#9HsdUzzavNfD2)vECJEBdiuxm_T!+$5zpkSe$YLax|8GxFBiH6(|O;j^=~KHEw$ z04vFj_rXEdGl^i1OgnT-Hc9Cc`ENMSvY4Zvj&2lRQa*qe*-rUF{gR9BP~PXlE*GD( zntDUh*Eq_WrYxfq%cDMwn>s5Hg%O4Jy8U8pB`1SV+zOJ)lIFV;<dG??qde<-3wD2kI%B&ExUr5OmSCMLJ7Mx) zV(JP4dh8Ywz|^?#8W79m5Ov-1ew3kQ;TACGYLr<6c%SE84arCsFT&pWXmE`_T583X z(g--H{4f;zppHV+R$`tn3h~x;!_t3Ukq~A2%PW5*z^5;dv0Kkfn!G-W8#y6uuRGB3 zJ=^t$zglgDYnHN@YTY6vld9TK z&ugyE9}=p!k(}c@RkMh!C%GI#OSYxP4&Kx1LLkU+sR(u|2h+)MOXcQ)cmlS2zS>RJytpL^jjXM18@_HE}M{0#$HKabH4Z?M--{F6Fnc;5&%ztiF=`%|Bt#Nn8V z$l5=!O!eI;eD1^{oqU64@abB*1NWS&<$9*M#!Re?{qi`M`;FM|ZS1{wV%0Tph0wnM zs%?bm%{o;6_Zgp(@LpBvBgX)58=W_GIPv2vzxlg$@PWzKBvh|IgVg5^9fu?nSJmdP zZ+bW|Vg__V>{-U1RXe^D&X_8|O0s0Xc>3X`a;`=9b<=l9Fr+h@iT z@f~R}UQaw+_EuNLTtP;wJe!tBT+{pSJ!@o+5Dx)1A1_RSTny6Yewz6{m{tNPl5z(4 zTwv}00tRT4TO4V+lz1}U%gXiN9zHHf9p112 z1l-#7Aeq2FMP$+}ue4E*MbXez=J4rtb+?oO=6(!dU(Xqit0pIFzKF10c* z8P8UQJwD6CQwGylCp**dPY!d3)o4=8TfFhCY<0F}^NhqXRwMx}{rb#*H~^s3%S2RE zh3CLNY4j}mfx9&Cd?hF;C~fc_)aQ@fp6*=BDu%ZrVv<=+b}Xj{KAubP=i1fE#CKZ- z_sSh@4?*LSdpRWDp|Wp!-6ey1{1B`Pw@2m4k*+zr)H1U{wHA)|4zeAa&~?hI?|mzc zYi%buM_+&w{{pPY*}kiz0nRvY1Z{?@HbRB^`QC8R=|$#}mW)o*T{btWH7S2{M=Ow) zR_71#;(lKP--jPC&y_}n)vMt0C8rkC&1=bp(=AZ?IkDR*nlB_A{|qb6C|-8u=`!y* zHXdYwwULv_*qv)3*CJDx47+fqeE;6oXOpgNzK}9n3&OHg3mQ@mhfT?OXDXur&SaGF zG{%BibBEc3JxM(B7~OaYxe{JBF9n5O!!LrbB&-CaGm=i2&oqnWTl4>{cjeI4x0XgO z48+jySK_9yk&iG{DGjGKs`EunpEnHzNCVqtCJ7CLe?N~nc01uO;CVSJD%8_21lXu4 zd>|wd#!d`*?RwTg(g&o)hoKJ0*6qg2Y#1n)&) zpI)=zWx84C4-MU^4Cv`T`rKlQp=aM%7;)gZg23H^MX7 zM$AjEYB%??f6gF>>oJR*YL9=2UuY@oJnWp)p}9%9z(jPC{0EIf`y(ae;LAvnmkK#X z%I<+L9vU@s6MXd%SwH*>cYMxb2b{IP1hsQLHU zJBLxg0XT^#e@ijjw^pf%O}#p>d^Q@XS$a&aR*O4E&xDvbysqJuEjBm3n9iFyDY3lQ{~O3-|EWpzmkW^9YcSr>aRnR zEkg?}rM)@cWEtPx4&b~a)4D~T{hH$3Vf*xHf;P_6MZT!E_o6T#Kp7yxT|X`ZFkabk zk=und0kdJegt6e+<9TeR1kwY%^4|PDDf@S0?%yP1U=_>&N+Cm)F%Su10u`A|!(zm` zEC>&-L3CTg^ko|xV4)&Vg4 zVlpSbfN&qco?e-ot@(>#M2ab2Jm_fdyyZroqqx;AilB{MKvX9qlg)0QP{OSD?>qp% z7vQ5&ce=R|Tt<6iMx7mRhl9wirGky2tGMS%MD*I80Sy^0D)*zEH29v^gDJ4d*~c-w zUJxq0rOcYuK#-Wl*^7y-uP%vt)~XS2;`+w`zP$sXs!poolB^ESr`p@=@NI1Ztx4Mcqt zQy4@5A*y~*F`PL0pgvq|Z|Pyj(A;T$mHiEC!4f62%!F2T*a3v6%ab+I~hu-@rhuapeWQckwY6D(6s~oEBc(DP;qQ% zdAT>C7XD!CNpC=Kxn9c5V4eC%dOgRHxk387{Kt_7_p2Um)hM1^)@*d}U{Sx`{`4=| zIxgK?gUMsVsCZhoEqKz*^<9v|S#KzW&Nbhy;Fsy6I{K#6FIiHhC*K|Qsr~}UU<0bT z;zg(6zW_-9lw9z%fdoOtA^#CG7Ns&IBNoAaC~w%M)5$~(Q9|Ww@RVaIku60CAl1?y z9be(LS;?RIaTsPsIWIOxadaO9WhNwzOk!#K@|V2T)uvHRGyaFW<5EQwikG$dvstj? z^CryZ{5z(G-NHiM393K$iNl1eq#UJj^V@c~5mbrsEs)2EaFST59<*w?-CA#jUEe%_ zdJU@ zmb~kn*6WzZ31TB|eP0s3QA6M+ekfQdJ*l?GLK4Z_%L@}?LQe?zU#Q5p{=3HQpXTlV zx%JPHZg8 zFz4?8@&+&7w2sQZ05oU$GDu-So{~g2#6NJm$|ay>vI}}3pW)oygk&8savX%PfjNJEw_;=81$#tFLvCGZ7sQe@Oca;3}mqQ+=@%^cMgy z7{c~qE^v9ryVru9unzq@hvkV>>6s&Z1>dC{3TpAn^Dy4Du;PmewwWrB;(06jsmU)C zcP0p!5Y2*1Uq4Gz_36+~972Hxp0v#hv@ITKF*E_k2IJzj0?4!h1WFsyJ9-Z`yJgvPZ$JwG{c<`z-U6J8P%CBt`s{u*58aRDVSZMJ zhuHx77JSig_;1BM4Q4nH{{ZVBR~O7nvHX2&AL;$1&HsKpUx?fDuj_BK3dYyGmbQ~r zJrOmkoV6=nHsNos=iTmzi43n5;hORZ4@2AaRVa#=)?@O;(-U8b$jHWBE`e~7XcNHq zFrET$jpPvr{)OQ26|cUsw7b%qWgZw>p3+&OZ_tHHUblKQ zTHAa#;Df^J6omF4GIG%c!aU*Q2pNPxeS2~9w#LGJFIo|jgEotei{eXueQw$D^skHG zMmcjbf8;20oF^b+28^N)kc?nMYzxb&!n=Crr{&Ac{ViIeRE(4LQ4gPYyqRBD*7B)v zvwPM?0O1sO;mWvb?mt^I+G#3!wsdF+=j=t+BD64C6$BaKc;N}>OE)%>wDErJtm!W) z-N0U8ovbl2-(zI?{it|DpVO8H#C6oWz2_jOFmM+JWNPV#PB2<04tm|PSmg}Zm70!~ zSEt)km&O7I-f=#~PQ+X7Ntc(rW^j~ya-IQLTiXOIST^9obg>+-{hwJ2=$-4BSiaae zYlwTG&Bb!+{n9RjCt@x^Dp#}qU?E)OCy+5=Vh7d_1_ssG_NNa7HTgxvs#s1d9jz*4$Fp_= zH2u~(2Qt)+H_c+$)IRdt=a2#;@4VAsFGg1^Wxi@DY;Xyus|i{E$d{2_39kKwXTp|d zS37#OI;hN<`p!zz-gn9B=74t(gP;Vqj%N(>c%ZKfrdlJ%=p*Re^A_LNG2%)6ov|uO zntwz{3a&~T@R8IFJ@ueZ7g1*Ylct@msry1ll)g_7ukG@HmEpqK;1KKz{5Ht_$t<6; zU+TwQDII~d15ua;^`X1!dTGJg+JGd5*1A8LXN1&)X-<$s;DSuJS6z4?S60E1BU(i; z2L$QUr=hofs=<_{JF3Amge4h@mYV4juUf(>Px+m2c3Q283m3kS>fE|`4iC91>axe) zIYL@M5eKP^W35t);ps$>Np10aiG1-ofyI{z8jpT=*cv6pd}#n4q~WsH?TXw_Qx;bB z=~$+7=ps1CO2>4J4Il*glB0_VrLrSiPj+6}k@?P`%b54>!U2&ZJFXLvoaBC0 zb4+a@400I5-A}CO%a^LAXxpyz$D3h_lA9;9IgC??M@joVKisB|s#~szDe(`SBpigo za10$-Vck%%0jvdlMVFsw5!Aaf^&-MTH~v~2Cxhf3y&hsktR+&bJo=6wln8g27aJTsUdk#og15-dVSvmuKXid6wS}6xc{W2 zjhpFwZ9gLzRSyV--@W=I-V8}^abP~;_H~PN6JUrL*X=_wpUhXIVyHbM#9Pzl^zkLa zMjJ1k^<0|4W$quLnb!gy5o&~O;&#QZB_F%p;%yBL(d5g)Q09r~S)2Kf?pWPE9`0%J zN{CT`Wa6M{)w$5(CzFwEtrz`{#CX_viHK1rp2>!0-SMiL3`Rm8$GWK4=W zGY0f>?<~gs=*;Kbx~Y|Y=Y$#KKg>%@sEX*3n4#)qRbY)rzUKl;YSoz8hufC_J zYFV}A-zk_fFctBG=-Jl1nsr%n4O?)f)^- z*Lr{1r?J^9mQ3&s&D5FZfrBLZ@FWLL#$oFCFCeuzI8B}>n#eL3#e zy!$Yuv1de_X2DiP7Ew8@FYIyjFwd7=H3V(HjpjTRDBE!{bIZ)A6S$EmwyZ1>Q4H9H zi{R~Nz>Jxi)AN)%GTjdorTs2ry=ytYm5gI3iei@bl8Lw1NAI z>>uKUR=&Veqwt2x%|5IlB155p;jXNwL6IWh=CHJJ#PQ@3coAN({xau0jNGAxWo(d4 z#@Dwi1gNROa}tH(W_^t1PEMvCphW9_lPa@V0)HtykL~R}k=t}+j+R^ng*hbj^DxHw z%e`^Q3Swl@+-!IIj(0h5=?+!aZcQG;y;!q*V{Q*}JIrY_&!PDX$T^zs((QNtzI>Vu zZ-gsBF1b1m)en{c+#V!Q4DS#+3U5xhgzi~?rbkS}8SbZTnHUm3W(%C*-cjcRn`338 zrL+5Z08O?l`s+u7erO6`3#XCr{$n0kA3h2sh&#Z1(LLD8I^6cs8Yi}pgp)(PE;t7z zm8L0hAy1(?w@AYpdjd4Jvyic>P{iag+QkgpeK6q2Wu@y@w}X^amV5q+Rx!Ruk<)l= z;62p;3w9w1CjLB%=I7a@-=TnHVJ`_!&?p<)ksGYkGcR^6zN^5?R4Ucr^?X%G3Yu5X zw?sz@=ItP7J}G_UzK(;93QTW1qV?IwN$$09zWWOh*`AM8)wRKOV@d26DyP-CJ?U5L zi*LOhy_>K}PJZCT6KD_Yi^&RwFoggaKtR(J8(c!a*^qw}1-YBPs|ENLo39^h&7D?t z{+>*lWKq0Odvjj+fDU)LOpTDSu#+m;5D5BbCrGZL2RXs&5UeiVQ7qZ2m*Au0s;l$_ zc%L(pUW4;AhV5DiG+;3+24*eS4Dzn-`ohkE8dE{|Ry_N%E4rf2vkMoUJDx6;Y_=6G zBflm5W>mO`P}4wSui3#4Q_U-&-yQD~7RPHG9;XQ8RvcMv=do6)SboG4;}Vem3@fWj9lNk~lj!2%I6~{+{f$#^HL}A!^|f8)#-1kh84oOX)Lj>iXvJLjB$&u681M zG-V!jM0WBqZx*DfHSMHwMts@wM+j&28-|;1ov--D%EtLfpo(&q$VhPX)q8008pV!{ zizkOuTk8I@X>%Sah>dOIiJQ)OiXTCZP$HQs4`c?qs~Gd{ z=+qyxFccHtdO=7yr{6gw=pzfu!y@u*`UKX0DL(yHS36~yI~}WLi&Pj!lWSu)N|`W_ zqTUN(-yev6&t73pK->)%n{o0ChE1Z;-|21%Y@!xZ886~&A;OMg0CCFF&|xw( zRm{MvrmnTrSt3ci%K_;N$Fl~eNF0ba@bQ~{rG1klH5_Abo^Tp4eXWF-#FlowZDm6K6f?c z41T3}NwFwMOte>gUOkeIg`$k=i;}jd3#Umz@E>OuzI3-=G?>OSC}X^LBaIX0Pk}dP z*3tR--lSbQEaB|ww*(~zP=R|p`f|T&WnNw7I?1om_@(Rlss23k^DAR3O2c@QTKxrh zckA3u`mMDXE)t=$CSi~@4C{GLErY5a=nitgSf(RVe%HsmIDMh(w@&GL&QC4yVe_+! zyE<%|kWt@&b#-i>Tg!O#tXZjed~~V7)tt?KGwYX2LYC)*wTE!@NZ#&}&KsovvhKqe zwmzKR=NNthi1iTptF$Md`?bi&nbba%niaDH}$k`fD(yFYDWSZeJZiiBx@>hs&(8 z!nx=++be7F52?0VGH;UJPkYva1HaH~!}&Sh~@Io-u4mi!<|O9vwSHjZYlJ z>w^9>FwC8f8uD(0JS+lNbn1KsIiV#3mD}Q~k+~bYtHOs|gLS`|>kkvo9(`gJ;8tE( zp2vz5>{lo`8N|}MpluH&wleTXSI}u~TnT1ZAEYynAZmIC5jJ(Gz0`HICm`@mgVTKi zwxZ&%rQdJ%73k=39obEyB{9__a53y!T$d(d@}8}@utnNd$nBpmJ=%aFdibx_aSrniqNY!>LU8wB4gB zQ%6G8ytiVO!i=VmAxpN1qLkoqk7WyCQ|B?_Hs=)D$>Ume(&=cq=!p+UeadQIW@YuM z@|I;y^k4KNLw|*-h@5M1t2|kwffIo>a3$DkW84fj!5JPWmR&W%TH+6ra1rIpla906 zpb8pt4_y3qRn^QbFu2CrCE$ys6oEdowU-a%8sj!IK_A7&q`22d7c39>KdY`UC>5}y zt1I^hA45~4Wx=voNE%$mZ6Uo zj3bBv{`l(CJ0uQ5ekU8OVJRe390b4&50KIW&^K!jxCHT_aH z)$hWk@WQRo;MjX4J)UBLze<}ANOrCTaB-N{OA@5pd)vx1CL)*n4NqK804F>JN5D8) z$4tWI{j`EJf86YAsQIQ6MB2q*_qFl+a;m#47XV0M7J4J;dd9}D!28k=EH-x>+j6l? z=%HoKyL2DHXgR@4J;EQT&ER?PiHvOWx*LMQ_4qY@i zn!zj8;|*Zb&icL!gB>N(q>DAIH6(CA!^HyOp&^{r1tcuux}nNnH7?TQ9@vMJT-yP5 zC`_H?V$VA7pySIIp!rE8d6A6wQvJ!2T3eCd=mLq*l*+|ZhOnh-G^KA8%1CQ~_lD|S z^3;=Hy`j0@qCaN*ik!C6eAG#by>f>=!%%v#B^+0auvUGB(4KL&4S$&*_2rJMvo4#% zV`Jvur92qP5#Y}wq&zgRe0h1dl(j8OsKwAqu)UM&{-M7M8%k;9o3{+oaX@m36InEv zO$~m~W@{zutrGqey~^&=#uGRe|3O+>W#b3=OiXiYTn|D3r(fkAZ>%0e7}z}xHL)rI zu_cZw@tQ&^K?fP?_Z)ELr{;|_&F7HIutAnUSr6{k_xgQKT(Qc8B*@4$tGwG#Uab$w3;t z)oa9fA^&y4O&iMc<-?G8ul{C;$*nxSnE=JTJS90aGopt(noqJHLiBUhntt0bkhxKW zVx!PJAzqiu(h!sFFi!x?M7doCJvYbsr3BmyL80703c&G_$aCs1AILpOZp^Hyv|ow* zhsx;RKrH_geqsKX|DyT7R8k(qL;sFwB=YaV&N^e3g|GD~VipYuH@rq(TlQkuwKjzD zFJNJV_d@Ik`LE{x=F2euhxcN$_rFY&0n-2YKj!ol9np7kfAlCLYcI(6X=H0($(`hR z#di+IEl+6v;2~AcN?-zvXiB3q!p%rJN!sf6_Phz7E5^<-{ZQD`Jdc1si&<1M5!u|7 z|CFlkuD#Uz7r+<;y8KZJzRU3I3$=h#(A%fQv}zv2r25Pi)-m9qKEHvbLbHXNshmt6 z%Rw$bp3FL#kkC9RFzv412;I>aekas0Dlo9$yRFCr6Fx4#6MJ`v-Rxmr(WT^?%lxB@ zt9w@b9s`V0ul0tKVqWqhc>1eH_6PtMlRKLOHNgbyEm#L*Up)vUcl>QBDD>^eSS~?1 zAkfw;AoxNZcDR5Sjmc(#^Q0rL+S=^z2~(JHbvTQkpMRNAwOj!)H4|+((!!a4 zU*94?vk3xv`{E7L_DA8*!ej@w%*x(g1xu8^)WL(5~7JK_wgt%?b+=*8ENpnz<6PAmbfQRkb)G9q&G zHlYF_w=b#FKV@VSm&`Vk^wxv7n$@|$oLDWeMh7k%EBm=ioN{SE$}lsV@zw2%iBkLDvbI}+2!C>o~xB?i8UwUI0Gw_O@1LKyKeC?kcz z9tIYBiAm$)yE>&uLbJN#{iRyiE~-+Mg{9JC!r8|45C(Bw?(U?8wVr^Mrl)$j9~dO& zl-S5CLk^y|oJzX6`?Y*&cCh*Sn!HMxV&Oxqx;xGs6K07a2#pwVA1<`QSb75Tb@rL) z=qXN}BhQGtc#)7lXe?yNk+mFK(9jwF@wYrixTtFM9%aZo!RIt2`U3!s?@L#7gtxNC zz_N!A&_gAI9N-ofla5!Wp?RX&NQ7;M<@6p2+xr2ZoDWT9`zxdBFuyc_vrHX&!&X9Z zo6j8Sdw4(mrVpg3o+&;S1 zYz>Q&eEM!vOgL-tf%sK$ z@?A7z;hG!lje%r_7&GD|cJ~HZY+k~h8ogh(rrjqa&YYZidyIB;i!{yG;1vSVu8*XL z0NG>uzY+M&f;M^mzWK(l>ig@SCDf*B@gxhdK|SZx*>EmaSci8=w9YDXdvno523rC{ z06Fz+F!WJ5dl`+3!_>YUH^ZK8a}d*v-{I*AeQsQq#!$FIFEo)Od0_J9I3m+NWAa|3 zjG`Ul-E!G;Zq9jl>>1%)3U%YuunN8EGP81j0ZDszs=aFrWv3tHjd;cDzuvAr)EH50 z7++uOGcwk|G9P5C2Hy5{F?p!dk|S2?_-gGggDBUA%Z}p{Qw<&;2(_w=KZ+FV8Zbn< z%+6Mj;&y}+?l0rhU^FcZqoefm8w7_ z)=?iD1%Kz&BF|tj!PaW@i90}L8(Q{}u)j|xKo|&}i}xdf9qho0^WMPtvk-rQA&=c! z+51UU8I)y9>#^Q{Y(z+qv@|$YR9Is3ipszRPu6MSl%I96Xj=}c;ODWV?fR&EMC&ZD zQ5?~opg_-G*Y0Hen#phUV8Yw=hvJULO`_o)R-ftv+RX`ZCKIB(uDG_WIUP_{yG7y3 zM5j)`SkIs_)Sz1%K!9Pf)rhr>E)yTQ9*R9n<*1c9Y+LKgM22&(XtT{CqRjAMM~dsa zcnz#hdJR~^vw57G_8U#6v|Sxib1pXezQ!MKG*7oj8SNYR9S|jwrMzyDFfFB(ORs4D zoW#a6#!5H5CAROM&eBBDG7jhJS>rv0~TY7Ww{BYrHmin>Suv zz7J7|p>8%`wKixRT&UV%jyKLRF0{o3&8RLcH51kaj|--=w-$5MpKJY#J`nU}SSY8) zLW0#(F#d)2OH54vimeu|;3KZu^i9{h2j6S~p`~@S&xKnnqZoC&0pQF}U>RKEI+Wep zK?K8{?-1i{wAJh>7K-?MU)nHe_JrBJx91k#h&SEu`X2aUMpY&A+njp_qbg!f$!xh+ zZ%GI|E=%S25B8%E9(xLAab8-yJSNDZQo%CVhIW)%q;2UwOR|0wyc4uPW+%lJ>y=bcvE*5^Ly`EETht;}JraV@X<1{3f@5S4t{?)-${&|3 z>F*_7i=7@rDRDa(AaX4mF6IfBneyL%{-e&fl(9U#M%+(7%(b9ZvCpJ8`_(b?*@&zO z`Q$Z`e>&Zsf+-IA2qb2sxZrIsSj%>OhvF7`BKVqp8thA+BIn(0UzxA(oh)B-NT%~L z`&HuT^6^l_1kWtdA@@jZv zzd?XZQP6jcA=vXoxd%JdFG-UO_~aGdRmkf0u7 zwnruujMQ0dl@5GO!ub(U0aB!ClaKER%;G8GNXIc~%N;JC8?>Ql!4#^v;XmOLI>gs1|8e?=9T{2%l6|1yjJ|74Z< zucU;;pV3e3ngW!D@8*3Lls#hWU2l+?(AQ+&(;!<&aa;JlHf&QK{NmyKz-yd{BDrA8IL zS?f?<7ALqK(z!Z*k><*)R*C%+xM#yVod$qzy-s=cP%!uW!Q)#eH9v`-v0N2BJU}(9 zJ-T>0xuYNV_N?rNzmyJX_D_bkntpe;Qa58)NH~huhx%YA`%+c4F|WbOm{ZuD>~Jb+UFt#l z*xl*-K5^|D0z9x|VZuS+9sr4>e^{#1rXwn^>IVaCUQdNM!iJ{}ax&jY9eiDz&A>V= zAPAJ}g;+Q{q0bz6A$##NDS$h)A5kInD(Sm2#O8t_>-<22M_~PmWTe#{E*R3wpf>9|Gs8(-ZI(m*bGcQ4#QLxc?O1iNvB8nyXa)8 z`YbU<2b8~k4&USoCT!4P93v+Ent6A1&*o$bJ`aOs7z?JT_B)HW2E{M8DSrA3;8thc z12VWbT~B-$&#=DiIh+`ep(mUU3-V1v4IXxE7{VAYmVIma_vi~>IivfkxsPi&^j2Px zx(Q~`XO%6y%p^I@nFim36VIUc8>}r95wq7T90q0*3OCk<*Ol0z9fA2LpZ?e=DbQ%( zJxDg~dHc?QP1XHs57r(N;3s(PJ$t=rI=&F;THq>aQ@mQ#-VB`Fdug=?Ge0n>yzbWC z8LvdqdBB$!bYxlWb-|s~O_X%-o-00ZnOVN5%65l^Zl|T}W|_$+@<$Ya4t9RZpzz^< za@CU^M;Y7#f~*M%Ot+EDK!h3^s^m*@2I%y5FY->qO9&-0;q1s>)v43r&2im`OJgb! z=aY`<8qJ57`_R={I?g6D-Ub%ked;6~U37nvwK?1pOB-ivFu<$=} z9&;I)qQY>KrZ@;3&*gc+y^4- zjV((JGYQE41D|mBOx|Eu!o>UZkN4hW@+mJvgREh<$_Pi~^0UXFo}SS@FqpTPO4wZQBE=~KPtPChY&C1UXya8+HM~TiMbVhV-Ev@qmbl- z+nxuTh5F8|Z9jp9Lgwuj8WX+3GsPE2@W?3*UIkn=LcR$FOgZh?hYD~DKbAkzr9|hF z^lN@4ZBsl*yAb`e`&9kA`~dF?Nn8Tj+!U=rRcO-7ynFxdy4yp@Dj}=o-x91 z8JJHKA?c46!^_O6dLn%p?O<=pY;W=?XfNqlZGU{`@CE8=wu-YgnL^mvIarVS)k(dZ zWH-H-KXYg{A%6)L#f3x?8h;uGP~@PWW~WtuAyHG4Af{ses%Kz+1}s&Bi|#+qoESHh z^E$NSJiSO<0 zwx$fBiIJsp-HvBd6$3osCvs;jq)bGJ7zX@Zl&|gM8Q2=lFGJ5yx*K9S+WZX|BC*Od znp^7&@1O(zsl4>6w(ob}&i1UdK*t|@cq+CFc+SuH^i7I5W39PwY1ApW<% zft)}O|8ofkVFK@_<}cv*Nyw$JV##y1@mROiZ_X5aZZV#}Gh{LNI>n8JWO&;G{uZSX9|i6dhfbrE110YRAH3Z$6fTOgJnx`pF24 z-atsqg;!C~yoM{d>?VT2_K)?+iH=)>v#`SqG54T`q?es~Ju=%kNJc$+*~%St-re(ho%?z1AH&RCTwbpEUZ3x$ybEdn9Xf!_z({n(I46`CpSgOk zq}^-!&~v+9qVmPvK4Vm7m<-Q;0B`*~)WF}Xc-Om7(NR{~Ci6@lc)^3|GoLiWIUKq}1C^jlxqC~fAi=c?NS z4@OdO&`lnujeX$I%YnEy)Ae{DiAh;h$S_C>YKz9Uti-vJ{$lw z>gX(`knmMN?q`49Jm>&UgAoOz>Wf3{nHobx9^QJ(SD-VIjA+MyV%;|LCbgd*4D=QL zu)O(?K8OP&VGiLJ1uuAi%Xy!;QQCBsKqJok6LJk(re9|&;#QAM=VPJy*%P6;RFjr&nm zPeP=2?r*FR?NmToSjoBRtmkfkgF|P4^$IlrNEpf9v%4Gz{AZ?LPw;$bk67MPD;6;R z)OAaA_$B!m&_2VXXD=XrV1RPUt3~>=^nMy=_f<0!9fJRVh$a;kHZYdwf*Z&U+Z?v}Dhisj0lAjLv`#Bc? z>eu{k_SGAd@K{k9#`zHFab?G)zW5^DEx%cJ zFsa)-)`?5f?wi<^A3;+V>U2hO50awbkK{HOam+~LNUAdqIZWbTK2AP`c>-aAIhh6( zk=54TuJduQ+=In~^T2sS96(wBfwazGJgdaaUIKL8KXgd{1*IYI_w;`cRD<*Wy``U@ zDnf-6T&24XbhIh)Zn?^jDeIgS5KN|@SgIb1HtD<@JUT1aSL<mrF&QwP{ zjH0IhHRG<9YY8*!Vy+MKtw zvGzRjeuKazAl+=;cCU0}E(f288}#XUxm0DDfbCyP+9{<6OB`pA^zEmXU-N8` z{0qPauprz+-L@RA{Po?K+{HzO*1Ll#zlhWyXfk5C!D`WU9~8EGK>F>`t*ZOo$FHLu zluU+#6E)g&{qWe_B!;=PriPyAuy(ox&k&stecZJErE43QOsC93cQk$sV`~GX9o0pC zR8>liKk5tuD2 zz6Nk)Q(dHkdP7p~-*(ksnSGzNwEQA1rQpW%ojIp1#?|_;*n6Oz^ihwJ?VH6GTXyk5 z`jaDlCj?sp^}4I&;tbu6^r&mzLrfwt8@#K*eMU_*Pfh(&qyw*1$>V9E!5q^2L}*H$ zt`?RZ1|7-4QRP&{DURi6j0zGVmbWyKjgVo+`1FcCAhcvr)~bf5LK^OU~f8)Be2^X*J|PglA>ckJ)Vh z;s~z#>&xwyabgId9r!4%kw)sn*p0T*tpowct z$M)$vw@HnP zjyFGLw~jB3nr?(~{z*NCzv{vINVT7_XVf8QwYc~;x@2%RNf^4aA~Ovy_T zRhGFR@~_&_ws=n{Y#C;*tMa*WvfUe|RB+f3h0hjR<=OV-Nd7%}$iE!_$CQcuD>Ku- z3~&G5->H;n%|DcUBjP&tDNKcfg+RRsdA4eqoYB@#6o|iFF#|i}KX(ZFH%p@bRVI-P z1R?@&)XHLHR{$2zAPP_>nP-h?L4{x)-CE4*` zKK7>>s&2(W$C9iODDU5@Hve*I`^OQqH)@&DfV0q^>@_8Qz|j1L?ZMsOphqJBI`Qm` z@g^pr!|MD0tCsrjU7rf_z-pIn*zBE-xyE%l(mzOP|CcxX4>;*+>&86C|0JE%Tt`wI-C?@#hHt$T{yq~h><=f$PL#|%9@X)w;d z7XP-e%?}^Cxl>M|b8$DJX#**>o#BYp9(u!Ug=X(VtvswB9%fpoWa3aX zj>&}QPw2HwR{?%ccn{CUJX<+T1yh9PaU_!xUGFju+28E-tU6-!2 z!gmQht^mU$V&ZLANSNAvb% zxjM)Fw$XKg)6J-3iMho(kJAQ$gcEJ6eoQ{R@Zy0$LtF=HaUC2v-*eswe~67dKGX1M z*3gUk;m?(X>eYsdVLl-!w2{L<*_!T5-0ck#ANtO1xC~-lg|m8UufUn;2bekES?J>LfviOVCNv> z=VcKe!;@E{Z|bsdmEf8K+dv+4%ih`bg@5qz+mPe&QeG)#8~$91uDdFu`q<`HuHA8P&eA!5lRc~N*EV64gt#a;KAxwm)ZxD4m| zRZaFu?1!lf{vMBtyz_xv$m)7t5pw^onl?IqH-xH$R-sgkJ9#*HrB(LZu%vMyVSRn< z9VXSsSOZTLNQ}-%XbYDhmO2URN}T|P?}Nd+n=zjKb-ZXxy)YA#dz}`kP5P44wn}XS zlejrz6p?vp?X_maJeLgHl;0eHtwr~**$@4@LHvJmoWGVU{0H9uM;zk*->~)mV{z91 z-kI5d)A`AI{`5lEefUpApnoxCAo)G}_sdoiG^q)wz6LjKG`7EW>611$Lal~B1{2wz zESnCuy;W-~Cz51*?Kn$2I{dE1c6+B@!Gp?YY4CB}*rNkcmBRweuEyn+rBLRDWBZSs~>89 z2C_((C^#QFBO6XNSvL}Ef8;?lHW@){Vqt}qY;N#c;KhT`(FF4xXX<`mf=Z_%yK@a& zHL)VM;lT<~tH5x#0kV`Xy z#oaG@4V?QXfCCe5-7rzr#t~>s5b}N*OA}9c4=ytC7#$cc6%{d3P^fszDw4ph^Fnwz zmeA_elMFW!rOr{)dnHXLa%(8OxzFXX1}*YuD zTjnMgjq3CAKG65CRUQZN$IS1~b@2Rf5i;Yzya{2^an#3KQ9*cRi;~2|M!IzMT4bxWBM(tZmVzQp-F~=GD2alg2nmI7Sld>l27IE z?lD#mNtS<~|J%1E#%2e<^mB>6$@0Hp;BW%G;>3#@>T6@DXj_8#@apYqWBnYgpU}Pl z92t=kf!)&Uxx9eXT^13FLHsAd!S}ZZX3Oi`kGWZTJ|UWpJGF@+H_VO+XvUyJr~-09 z`0cy*>@12nEDW#trAegsXLm@Ioj;8n@315|=9TzW(oXk$%Q5JsHEx9ws^Mlt7&tW% zh#HWiB$Z}P^KT>M%ljuyDpd>(pC^f?h{4|2Gbh}Rrw|ici&1>(l-?;%J+Q3RXHjXa z@K$qTqs_y~!*AVq-;a`8P{5N~7GfK7(h+p2R9*ymRdq&{Eu}7aci5nk#OOk-h=Z`M zf|`}-L_K8LDRU%2SY@1rp|srKvt5g)>i1$ciuxUca&K=X08FGG_MfgM|Fp!=-P>wh z2CB%~e}+T)*BB+dVc7=oRSLy#(5Ljqo8U)ojGvCKVrBjqjSkA58rH0y5B&zEJk38R z0|u)TtTim>^uIyLwtp0uKk;s20rQ=|S`pjIuDX<0(~JeeS9Row{=Mq+ALuv^P)9my z>$(EguSO|?7u<2)4xHg4`K7mvNuxP9s4PP-|7Y^g{|%zqYCgp{u>qhzpmi_QC|PeNz>flFDGE% zSEMNCg^FL|i)%KI> zCek14v=ncJ|7R1a{|RdUg&_GqK@A`u{bz&a{{*%FOrZRmL2dr;7}N@5?uUE=3TQRc zeqfC9;5)G8=yCA<@y`3Em%x7l{rUuWpYG4EY8Jp#$spj^&v#Y8Q#HcdRE04PFP;D) ztoZ+D?HI4Zq}(DQ&~<5iY2gXDgu(u_0B=%GeEWazZ2xCI?!VXX`~%LKu;fO0reE+j zqDz04O$D(c>^VZ7LdyO>Ef4u!MLJ@2kf4Vzji~5{a;d@ZCoBcH32Aj!49d^h|J`QZ ze<95OAD#Q3z>*H>f6kob$p2b03fvo5m~X!n0*Bnuve*8~zd?mXux}Foh7RGsfXDzh z(_ei}`PAjC8l!jlr}Q8@Hs}AsY|+W4LBt6vk zx5xD}c7DBj^(gGMSfEE+gIkIQe+=js?{joOVwFQCjpF-Mw>KtyVmCOU6Z2}@HEaAq zfO%pnoC*uCmzUhMhtzRaZQF!Fg&5z&-%+-gkt~1-;?Z*%8FOaO8qq&VlxWo@4cb$jSL+oZ%x&U2z%W$ zO1p0XBocfC2-0X}fglsVK_ez>=Tv7kG8+Ro4;tt3BIAF6J#|GWXA(5{=fy`oeRF&a zyvY|AMAOh~95~XB02il&=m1KHR#2bK*!h}qt|A&o`t>uJFF-^sdenI$8HLV=Uh1Gb zWmiB`lFB$G6*aeV$kp`-RPMHBgS?cV6h2y!m)eL)DaeE9RhI0AA07-C5_HqAN#38Wk1Jey!XwJ8Qt*MZ6Fq7Jk=$q~aY4p{ZvmnR zPRxF&gFCrFk1Xld*y;N~{(6swl`8sFmlj!t`fZ>%KWJg#9&%=*SS{z_LR^ywtql;?hGbGEnUC*pUkyyC2yc;Dc@ z*rL$pLr8f}h0_R5Yw2Bg8dzMLpwkD#{g(P+o_uc>_8$w{$2?oqm-^r+nTHTZIn+C} z?8p;r(G=1S+J+oD#%hr7t(3Qr;naM2?PmNw)!lgnU}LNPOLYXzIPk#ifvI!>sp6BU z7r~#S_6j!a&R}I;)N_;0JkD147L7pDwY$uDp^Fq)mD{jvW+`XtfNC+ag8KiUP5XMk|^l$myT+7;_TsNj>FwVki#tKZ@Q z41ENl7N)(%fySn!2%di~#$czlNz$AiQo~nNV~4Yk>tADuP-U3ueeX*xl7uzDLV05L z1&xFJYWApUs_q(ltC0BF3z4-T!2!xnjO}?e+JC&)9AmJ;GW95A>(wviu|4ADUlO-3 zflyFy>6P5zDRZTrfeq#@?}t>3vwC#h9RhRYv*G2uuHwxki@sIF>gzj!eJHyun=S=X z^1=F{&LNQY8)TRvU26jLE9>LgdW_!A)C;sWt>t(X-1Ts+*b4`Lb<1%B3qZF-QWG{x zpw{aoh>jbw{8&*20tv~xJT6}boMdwdgi>4=Xyw;_&6fj~eUZJ1#Cr`Wh=*DpayA{l zz2xdD&U9Wt;+p~HXt-zmUGTY;Ji}E;VC$86I#hOp1i?EHNa9uf;Q71|_00YQS0fHv zy&m;xjE3XxM=8H`N5WhQZZo^J*`pp(a(qv0L!<2hkf?EIA-x_f>x$KT;Hkl&!L?CUT_GEc)FLI*bL$>WloPhQEO z*WUuSw#^7%w9h@`f{nbo&nlKlO1e20I5DUrMoeRWi&DOA4TO14-Du^U-TLK+#gjX4 zms3dI7{ow&&Lc2cYwrqDfgb+or?w?#;yrOq!9;*jBD0jD7d`$3}~uEX&mh? zB;IV_S58t=yBDx!z^Do3m41QzOi&ASB9F>Ugg1_pw`?H?B zjl)JUBe}_sHF(uj?9CpY?vo_*{pjHNjpYDE%&N`nW~P%EQ2Zm}m$zB$#+ft3Z&f`F z?Ry&;4d;g_bo+k!j>tV#m+5A@!7&bdyxZ0TS-6 zi4%A7fb$vI^UoB7vzG=*j`_50cMA4-i?0vq}g$r4T!e*Amh7A=r0h!LM|9dLT-QapdsZ*&muM-DYo8XkScp{7j@hX zXfn90M^GJ)y~qJc0E^X+%8Iy5rsrd^k|7QS(*d<`Pv1rdabUMFWBkJwmAa9FgwM8| zNUi3VjBY0PgV8;mi2ici+*dvnjwRY+bA=EF(IPj?UC zLg$69UEkl=n}g}7hY2JdGw^BLc7;=8meO-+@twhj^-6OmWNC8x?G2%IjNGKKg6PWD@KEa|MeE81F?X-HF z6jRh=gqtL#T)9;2y{MJ;#ey5)YFglWzpsmZ#d0CUHQU5^nf}c`c?- z?_`LvwJ<;Wn#Ee%xk35kiA_S6+4w~j=y>qv0Q6$4LLiXmR5EIox+AgR$jon}BbtE% zB_1RFdQYg`Ta)y?BeaM4GgKTM+bzR3V=+BvH@a`7^!_Is^&jQ{Z_o)psgX&1Hi%-z zfY}{5KPGeCCDQfj3iHhCIJ4)q72=CGU&H_iOCjK(p4g*lhhzFcl{jA@#fFU&JBM73 zcD*Zg-p)6~vv|zMtuk2lnY5aWX!RbfDu$6e!2-DYA)e$nPoXz#@NDLy}PM2aurDFOsALw}eL?&#!!`FZr=H&z zh-bQ59h6>Zqi1obr+K&=nvJPM_9woo3K(M`ubl%U)-qpw=5#j*8diROY2GV)Tk3)T z+F8h~{-TzeCTb+}k@?6^Vo{4VWROg72!v;Yd{LrJefSeoh;+Qdqk}e5sd=7<@q!s1 zb#8}_J0#x*%x8~Y%|o$ZChyCoo=hx03A?@A#LtFP2G%ia+&Cg|FyoyxSNmnYus)_? zAPyp3q?a$aOHbQ>!!UnFUDy72m&YdPqkmi#?qOD!O!|BvsTmSZb)DPBRf^$7uEy1= zQQ!J3f6jL(IHo|_#Wn}uQpj3F7u@2Z6ix2qaBUU5vSiLfA2w?3sf#B$MT0e)3UEIy zEP`2yW*pXwUv`SS48J?^&layK`SmgEMnM=oJ3o+5?+6#0g9@)ze5mFAEK+?p zsVY7&hzKOHIiGW-2)OCC+1GE7MX8W-pt#EnfW z{mJd@Qb%e$HDDT0NuNfp(p_nV_G*0tlJ91C#H#Q8xEfl#(81FhnnfVhtdb?=mbz18 zr(-B}0P;WV;QZRa7iC_#4a@^f1Zo{ma1MHeRRe z%Idvs#RU8uuER(oxm$GTR1D?dYA&+sDmmDzm6#Hzi)?rMD`1`&p+M7!dZ> zYN&7_qGEU_K0Z}&5+SpDRXG6Xf~rpk(0&(b%n%85%-)p!re}pvKW>DgSKjF8 z<0Up;gpBtZ(f7e)dHQ~S!@l{IF4=IYiMPbdT(Fb6L|nATDbl33cw9>)*a;JhMS>#| zL&`AkzUIHmpe6*wdwH1Fn+F<8po>oEGUQIQ{x3sRX}E_-9P4Ota+*~N?iRJGS(ql? z;*?l|JSjCBz4Y$>iD3CpZ?|#kOM@stx<1PvO{;1gJ0dcDMpf*_DTr$kwJ_94XHfqf zZYOIWnC3qf7!$-mbsrI6?lG0ks>2@T0hm3>T;5Dgb*sgjN;MVU4z>*Pjtj=j4`c&k z;r5h(Z%!hvy#y`T$wNH4%rIhx*2gLy4nM^@uh|;mU=r9#*kK4MXlOQmch#paKTgC4 zfiYu}(H=dkB%a3LdwW_{jhTD{TT14y7J1wf@TKO*r=WqD)I#GPWFRTVdCjgbR+Xit zLXpOQo0azkaH$+0?<`Q99Q3`=jaW=lcMBjxS4Sp5`O7f*@@@JNhxl9U9U@X)NrI>4 zSBgl&KoUp*utlxVS6gJLXZ62uFs8of19^_KD|$0s<+kRRuzbD8VJ#~Hm`RnXU^dV$ zeMzPc2)t0|L#G0S(=xq^m-EIJtPo0%JaE*WC~ijQw%QB!HGg}|B1!47K;jEJofadH z!cI}`Uz0H(EB3y_6rUhA=4a3=!*gAIP?s3x%NFKIOHyy&*m#$GiM7aPBKS*4^ZtO* zCF4Lo1t1O2dy6fmO^s9e_I%pwm0wD`k%L$RKrQ($lRryD1g~DjoFJhGtN-l*;5`#L z;1WyY6NyB7R~Ibx6m^@aMSn|Tbya%%+WQn69*#=v8z;t;br$uHzopBzSIK?vGc46f zaE#Xu@|_y4+LExaW&3bWvDO*MwsmJ-7?8p5 zND=Td*8adhauALfh%=q%}G+L6tBCXVdT`=QXmuH-e~(@HZdk&MLzi8gnTP-gYjnB?)2-$k!9&SMbrquKHnz+cguF8NL(>Byv5lBE%wcV`4s#&birEmm6?aAnNO2^ugYTK&l|Te?x)a_` zt4P6f8-<4ddio9Gr-?a3Enp(HUQIK#HJwmKW6e|lCd`>P39SVE>&1IYxKzhBVn-D8_BGZ)HxEd231GSFqkTVRs1#v1%E_6oPtKVdaVtTL0_!w6`B~n} zoh8pQXNK3$#Ch|380t?LgJq<5v*)}04F&+EXb30nrGvG^}&+cc1{B^l_@~WHy82PxvPu^9|DeT-yK} z;mVWY7AhmF%33S0OQK&o z7I8!R;ieh^5DFh5c00h4$M^^$GNZ{e2M#f;03@e~q5r-Eem>YHND@p+sUgnaPn zRivW^%Sn3*l3ec$NIS-(=tf9_rKF#5<(}W)VS!sx%Em%Xiy)k{E?#fbS=b1h4tgwr z1xhcuw5ZrNDxgGOvyD@&Qj?;%^Gu<}Eqxp4<_7i#Vqsc)3^eUq>O?YA-#m8eYq-~W zy77^!hH=gIfWF8vkGmCP_|b%U-}{sF)Ke1|KQFInYaB+eXdsXLN`~_w8*+a1)8H9M8es1V>nVR&_PaRic2SR#VwiRaoo|EyM%Ro!tNwx)?aH z)<3tkeIn&&q17-*XDK~;4!S(*xnZA@Qnx-o;L+R<=@SqQA{GZ_uNdM`Qpxg-LhaYM z9AS|iwDtbDVQ`G&_olWCz>=27%lLvAnN@Q&BBy1T@_oDAP z3vjHVV}Jq9Yxo9J7lWAq7<#GeqvM~4FdSOkDt~inVKAVE9zlX*q|A|wL>Pmf`3K(F zgikx{{E*B%c}N-rXWo8K*dQBUekHu%+K0kfDQ=4P>89zVAb@UJ_~I z#a{G^Tvza2Va@yoah}N1UHQ&{uxcvK<<~)`7s&kr4|8NV>h!+^3EsiK2rUZ}G;G%7+LjyjbSi zd67GP2cqkv@$J<)*hg9&N%D@^@Hs)1;!*)(RTk_Z>1r#Y!2-*GKqBAiA+5BVf$MIzd=pg8ty&w?I6YN#LN&; zI)CX=_23*Ue~&(!$MvP7$MxM=#_{b7z5&AhfHT#3FlxN#0mI{YXEI6M1!$R5Gts)t zg*2fi-iMv7)dazq;2P|uM@`y&V>npHH(p;M#(fP8eAAX_hDg^&eSrcn2eIoW?%2_X zKF6Vtongkph;L0B!z`?txb>}sN!zkNZTd9)6=@@-PAg7?^hNfCGz1^tkzH9{MOQS6 zwpf?HeAN7sK7le3O^yf>S~yVODUqbJ`KTI5-zE=RbZWqTB+b8gQvT-XiOWG>U)a9X zv*h$M3M`xd1DMAxHc-NR_~5Jj zS~%UVZBs-7o}M&n`@Wm8x3EI6cW}6;vrqB~u53_d%@vQM89q=}|N30I@wg^xQG%C`gF=Jre z#3v5UWS58EnE1I?k7ox_|AesE`$YX5FzE_ z#tqN(qlVXWK8~aRO1R@3h@}fPMjLdmP|nb*WgN>kt~F~fI0Yp^25JjPbiug|nFWy5-cFs!4Ewa$6=`7K+N}><6&O^Y?FQT)&28FTPI9+2h_6O z2TqG7&a7GJ3tZnZjw&^s5}*s;kK38g27T|1@`#|L(>|mXO?H=iecs-g|H?wK{)d==?~Y| z?ggaZ0&hLc#a(|m#1LMg82yUDC?{Bs7?{9gzYLs%Z2)M(Ke>gc{~U#g^?UMvM!SEv z(JuR9evXy4_B|?#(*lwZa8E5T8HqVR(iZ5k$!3`4=+1l~vq68?ADF;|2T-CS%Ka<8 z^*i(Iu=~8IfPOAWObSj;dRw#Wxex|-#gVBm93d}A1RCJTBNA}UZp%RY8Lgiud4qYv%VCfE5BXF^!0%&05j8izoT5H}cH8)MTb;l}@*kAf3d%&g5D z=I4^=OI%`Gw6I{Re8Edysuura$G`?mY8tRWbaH|3*TjP_}n^@h;crJ|+JIJDjI-tj)tIQIXpN z)UZUjw4sBjnbaf^-;rX*#3m7nzVe6llK~G`PV&-kJ8!$#vO&r3NAvWP+;I7nn@aFM z3WOysog6){tqOEce^b?iU81Hz^)QqU8`!o(;G_#YmA@1rEjLFO zjrS1WzHqr$fY#zJYGThn)_$6PJazRtNhz~Ig^amEKwp*5Bf)Q88fLs7hzA#OV7%va z`t7jJg_OI#5`O|I9ISM1>&$;zal?_{+rcQq`rX&MK5j06HbPwAgV~y{oF_~oLDFYC z=)p#vZ)7;saXpaU{F|~*q0k&tZmF`8CUuskClm2q-RrUdAxt2EUcVzu>q2Oc-1AEV zbJTDzQ1({%p0ClIgX4XFgJPifg(h8!miCQsR4D}IofwQBEuIXHDH8_o_d$3Qq@LbL zO^%1iP(f^tC+pwbIgC0%h85wOVuq+e!E%gpP_Bup046B?)B#jou)kJ~oQSmNiE5Cj zZGz)Bk|0mE$@K{rU9cs-bsV4mpk>Bjzr=jreh0DF492M#X>lGEr z0IP>t>N+94u{vrr<9$u4@IlKb`=81n?ZaKbljNx@oo&htWM%9}n z^!ew~y^iifQJMTcOO5Lj@Hg+hX`^cliF=Zq!MVa-)+y&FqnJpTJoYtu4k1`x&)qC2i+DU(6 zo)dWQ<~CFb9FH(LH;_Cq`j+!4Jc_Yh%9bJhLN*afzCo{`I$3q9Hp^wt;j>8XjL)!A zhqbaN1450}mUTJ$Q?)kMCXT3V=~*#(uEiM)t==lo!~Cr7p={H4RV=$3jZe@_#{0_L z_Ph7IKAfSUeuj3mC4y2RxNege0!z9fxAQ&BNADZYzI1y z6O5ly`}~W0cdCQf$D}nRZU6^^ALxRSjdl-R3=C$}&$E%Y1C~=}FcR}(@dgK)=&d3+ zk9t=);h!;nwzh%`dnL2qbXZ8!-3E66@$Pje77f%~aM^cKxnY(NHfQiEKE>HiJ;;kR z*)1*kX|Wj~Ck05L^sTKDuI9>Wov88BJ$q?ICXra;o0T;3Af=X1q0hCYqsRP~qqcvM zLF4j6fUx~?WZZ0EB}ll98`;j-2XHbFzK*I}39iEi%>YuQWUh`~>iLT48ZIEGMBfa6 z2h7&5O&qzQVD?an}Ho9Pa{>@kH|P+4?~7X_rC6oufZjYlgTt z7)-UvA>J=J|NU0Jb<&c$XUsmS*opw&jyVNXe)6nb)-IM?duqy12<>5GI(tX6BDQrK zW`A7(RjsyLB`Gr}LW~G+-stzm`!o;+L8KuCBzLB^X97g7%zvq$rGMG=WsgCUC#1@) z`-&)UnQJ?)619mV>79GbThptLp7^9aLr`Q=Nh>*KU`W@v0&hVi$bFnAmzUI7;6Flc zMfxyJU-_&ZMM?Yy#d-RT5}JlAsz(hQiNv~f(LomqhIZ{j4 zmLF5_pPqsz0#f_2N2eXw^akMiBs0@6L=g5unlEFu>*^QYU@sQ}$a63$Bw1y)UQg!D zn-W$A-Gm36{a=i-bS5l@NgzBJdW2Lm7h-TlOXke$Hup7y5Q~l00^FlO!y*0mn+jt;6J&I~f zj7}4;KmUtYeS2ds3B4kn8WEm;8b_Q(a3iASC~*ZB1&fq=M1P%-PtoY1p`gy{*oIvN zu@LO4O8mC-Y`$)l5rc5j7<+k8Hf!x^^R%F51z&o9ro&!Z<~K+O#a&;Mi8Hvpw#oiP zZlOmAYYIw*6+pS`#HfQEC}>vykiU?8%+2f-%6sm7WF2rZb>P(62sR)Rxe(rCvGVw7 zzbu;z;^+@BOG}$opb2v>%>5I6hx`0kBF6EVjm(?spaRrg;=I>sW1?=^VeG1fuSKbJ zKe7N?-&n};d;J}>HR1g!nV%FL2?Nf__LG$B{S3On(Samw=16YKo)(ep7L7`)y_BE& zD@xm(U?SzwAWnK8q-YeBdLz1^b!sPH<2Wfp7whfj)c~RmKvnQL%96r))i<~W)$)}Z zr)(_gQcLyMo`=~8h%o^Uq3c5Cq`M8P_q@E@rwp_piUXr=&Q@ZEBu{~RKm*DE=(pwH zZ|_S;c77vF%ajLoBuNw4JlG#XHiI!nm#{D?L!VMiXpc|OPXP(Dv^#`%7w`p_HiSp6 zSf+)?0U25}Z}0XrTYdeE8A9dT1QN{cO<(Nl2H?~l=uMw4Kb;?$=Grm6qy@4klxSW(sAr~q~{}!!y+}` z5irHTfc9D`v95;ro9%UrC0m7vU3?MumJA-iq3p)&^zCoh)r~TCn=v*=pslfTu-G!2 zZWDFd2E8pHDQr_l?e+BfDrE>SM)NpTRnLhhCGE3#-3rYeklx=&jLLh6BI#X?Md0sL&Z_0LHVz2rT&;`w}%G$njnY~AM_7RMU zQjF;P$Zuy~9@XROL_q7eS@oI**CiS*bS>O5D ztTQeSQ;)e7xT4wxs z=j-Sn@0g%(*VpH`?KZ^F?8&n{ao&skjIDZ(8zhy#K^(t6fbIyDVq0@XJ4o16x^V9+ z1>-0@eA;YHcg=NhEwQpq%jL)M?q@|12c9@ijXKm2mD!6h53drsHh($A21kpu@_svX z2Igmg+Tik`LMH+r52xN=vb9k7Y*x=9z&2hZLhAw6ZZQF$yi!U~LX^62{s@j@&mGOnP!8CHeWA7Z0 zqQ*5cd(KxLqA9N@UjqlxPOab$= zaL-4UF!KrwrEb*<&s$`z!{=kQbaP+KuNFj$kcwl z_44=bUpZD;58-3YQy*t$<|2L>0;0g`44BNu3qwwv0<2;)^0-Avg9>-{9Sga9R{yS9>=#`!ZwWRer~y0{*e1GGSC>cOZlPoc_d>3fd>KCNf$Wn}l z%{hL$RJV4V@*=%>+t0Hp|Kjm?Cj>C@sJ)lM&pev=ZcFm2?$Wt`P`iv2S}^_b4;=hI zD&>i&+K+J|vx8^*+wrxBIbpz92VS*s`ITE>P1|G}Vrx=ygO*x;a1aLHjJ7td)-qmD zeA5^4fK3Q%^CAFeqKygAj>rd@1MagvzZ7>>vP?l(0HFQmzl;HZJ(qlg` ze{stF7v#q|{2MG%24gGqlRw(<(BS?_NYPjfSFHp0cDPU5mTm?a>27k&uaoDKLe)(9 zwemSTAI8nAm8&2=Pv?qbj4|$LGJ_||z}aEJzIYMH)|octb&L_a`qx6!$*2*PH8Qs# zF#UcF=5$&OwQAyLci(+3HB5>6XM?Xn_3pX1gvo13Diy8o9tYZ{ee}rBBXb;%==W?^6h2$%7Kn(TqJ{RZ zQu)L9hST1_TWM?X^5V$+=}P|>h1h6%T4&d?gmh~KjuTSv^6wnSfyH+dfm#pB(EtY{ z@Q^s8xFfIMK+C#Dpz%3??IBv}+k5oKhtum{yfgR&*-ZTOVn9+~mGXM0`%Qbiv9m=s zop)D0kg1qv@Oj(996)82p|X)K!~W|;3unJdJ0xW&5m5h6=z(6WvhUdz)53%w)zfTs^zLzg7%oYx1GW!L$D`HU|FTAUZB46vtyxW7GVx8{tt2$72oMR zucl!W#M9#~YLZy0ZS66TCX>1mjx#{`K)v;CoRYJCJ6VfGk)P39MbLlx?9RK8!?`V` z8g+Vw+LzDFTQ(d)PJ6w3AkMeTzAOj#qS;Q53@;9(rqU2Y0Peao*G36aC#6kWv&4`~ zZ0bgBwjQ$%5?Ep^fb@tep^FNek(Im!bO3L%O~a0`>b z+4|dicM<|?*~c#R^b^~xvu0DJ&grcx1E{Z`9-{|c6WtGXk7Zm_HkFPO6&jFHUeX9R zzjqp61jE(4$p)uS0{fe^dkfy@P-T@$KI1KW@j5cCye2`FDdSR>neL4iZ{0wHhHvPe zDf#WN2sFxVVk&?cF(nYSw6uyP7N9;$?no61tH_n0zJ5F|7Raz`$Vd}br23kQoT&(0 zoV|-?8LJ(iTS-1Wx^ABCYNx*DTy4ii^mklb2z+*8n4Ihyq_-*A#>jU;Z}s1|!aeEX zDskQ$YvaGkKl@|urY{@4v3h)~C4#&;%P3Mpbeg;OUZe3Ki#8_Oe>>%pq=I&oFqp_`}{hQ%??oA2&kD zsT|=X+ca?`W!QV+>xRpM&w~2V7u(hJp&#*alZ{B=HG8>h7g-1*S!a=`tNqjF0~b1S zT;v!xP;YL3O$!e~1g=ot>E3W`RT9!3S%F!q;nJ2}KdqZ&Lim@>+teLPz^2m4@fxK1 z80Uu@OXezi-oAB0l3TwkDAVX$oENQHU4*91gO?!pB<55*@7Ow8OdY;-+H)xrcHz^) z0!>GGNhWJEvEOyJu}%m%G3_!01665+t@DLHwaR_sI>^HPMHMg*FRN377|b)V zB|Cp-V0SU5gk4PdOsmX{&1H6&!^9m1a*Vk@!oRF}bxSoHdkm@pqxsK`a^7MF&(-_{ zta30BNgUQ{h~Tv%s;^4jV$S)MufGmrHC-Z0Qg|NRU3FmV$`}qQJ6n7>NocfyEEM~= z{G@mz{>gL^=w!Ved9469R0-`l@mB9iOOhq99h6kGq&Vjv4j7+%SYP7xsuQj2t6{TF zW5a2Tjo#yC0@Rn^FiW5{s@)X?h>F#vD8}F>_wn`WWfYsQKY=EOcvX;b&)7~9z+h*J zz1%HmLRIO{dcYLo;pX2(Opgx{`YK+!7Z@x-1TVSft*@?E=WjWd95<{gmN5$j9*SU{ zl(xsiP5s20M~A~Xty;2|^d8&S`wG$k=wjfSwSNjjBzKtHof5OInEO;(@|kk7_U!|O z+_=ZcpP$3!L4r-_G1gU=DS80)vdtV(3in_@azBN_JxG2QNsyF9h<)Zwx*w%`u9wGl z?_5p5n^{KWeaKT1FnMX@Vxw1-Q#f)A+qv-w2C#gAxm_J~3*J4mP-FcMTFk%rMClQ(d@6rYD~=JYs+0&YAtD;w=a?#@I6$^0^<)zUCVN zzZq?riB_VF79v@%_W{=1pnlMiYhide{}FBv-~1l)!HAJzNk7ZN3ckLiFkQK2z=#Jg zXmH&h&L1nS);M-h!vT_$Qmb6bUGFyGAIrC->r(2rsw3X6bK+kc!HFusj2dU=i>h{%>*-GX(!S?=iV(T z;`V!&4^xQ4hnlPAniMC2_cKUnhk8Ni!1Vm(Q5b)g#<<+=*H*VHc8~oHrTP~!uIFF; zs=;9&Pv|{A(8CI&y*rl5*Yw^n)x7ORR2n08+3cev7g${CS@XAS5mOu+#!uUW&2>sz zO=#?C80{@2{iHX{$Z;UQY^=YR3i%kV@Pmme1=RY}kLQ@XF!-ahqBitz+`Fb&)ZHEg z@;btFkFA9qUg5`YniL<~-Ps!spWD^zTekHzr&Z(8uE)u1^)Br4Z63zC7HMp63O+bo zAzfOBynZa9hD&NL1j8|1D-0963;(_%dr)?Z=&AizDxmRxf2lfA5kxaxdrq7HMaj(d^edu-heH}f>65*L=lYURRqK{aYhU;bD9?fEX zVpeMr@=hSJ7iag9=y-Xp@9d_CLkYt`>&wv+xZ~HPP7mYw z>>II<2&t`3!(IwR3PCDE;}yRT<#arBV;G!_gpr}hyEgX_D;5VsrfNd?cBn{L@+&hZls|5 z$%&!S+bvLAcl*`7yHJhK%7ok%$_;Zc3!;$*uL4`jdio${%jnC)jS61o1E2=w|LEQ;O+c7w3tERCpUXL_WNF@`ax)F&!zd!yi z?a@OBU8^l~l*P&E7r-y7jPYF#CFet(MddP$xsPVT0Ig;8aXc2{ZSQ3vI132vkxd%q z;P-ub7GBHVWhf4^n}oY9D6np@ltonxe^_~)(m|4Q6hAe=KScR^Zq>Fa3^F+%Nm2uT z3?sG1fh(pjX{)LprHaa!Qfgi1_B!jG?unP&w$$e6HV=|J}lf2*z zuAyxqx;k9DE806scNl!_4P7HO$k8kJ^t!v6Fq#G5LAH`J9mRdzV7O6G8z|2162nM~ z=sHO`zC6gP=1`K7`;-`N&4ap2Vb$c@>=YTmd5jyGQb%(+zvbEJk zHX;w3$s{U}7G(_~^pbnoSr}*K`So!$DE{aLVwH3#AhyYCuTz3_q=rLkU2Y6!-xy7j z(A6vfM~AJ48Vz?k1&dgReK-_zD4CJ;lk=f^ATVKNsLMtR^K%5at{OWk-oHez;DcGm z8vtfOC@F$p57AbtX<#1uUMo}bTG6`k5q)NRA!gdVb zfi@d$_#9poxh?X1R;W=TN43;(a-J3B>V`IGHwUfIHf=y(sn!{u4=FI%yg; ze~(Pv?VK)4#BDb1@lrZ*JXmchoJEb?}vlJ)z94lv{Pb&>x)Ro>oqoM<>5mNdo5ft}4kM**N9ZXl+`=Nwh@jYbx zUpCX~Da-Jll-3fy%jHO0N2V-7=9E6hDL$rNk9gw@Hh1dBloOicFgesh2DCCO{Z8bG zOxK5_w<>RP`TPae7K~c(^%R`&G>(5Xw`s^d|IR;x*fTiB?Wd-)opTH?Fw&cwkJ9?O z)5KeIgwD2$PPE|X0uN|~Ax>oxxw-mEZe|&i50m&Fc46@Bt9T*D)j)>)9wU;E**ZpW z2@pSlN8^R?%|)Q_mZh@tNTBnN|`Gg`(u0YVop&bGT8VM6@2D*(H4oReJeIlclK z1|2gjhT0hf1zs812k?yZ-C^kM+D7~fWv1b-k1zH*rC0Ts93287XfMR zSi57x6=EKE!%pI(_W}^Od@=V_c$~lZV_vD+8nhMK!eVKs6#Ob(o9)v2Jj_e3tw(F^ zm&4{zYYS?UjQSl4BeXT-SF98(ju_VN1W(M%xE6nli-@1anPdq-flc#mM zs#=U^(KgC_jUbR0&-7smFtmRY?ld*oqHrl-E!w z0ekHisqgCJexKiPn82|7Cy z#z~G^(bqkRj|h98E|d)hku#B~FSUAhB-$+#-r_8KJ-irG7FGY4WL7M)`da2*%u_kB zWD_~~5+z%Q32A6YHV?e^;G+E9hmLQ3GzPMq-xbhvDP;i9K$Khl-Jig{TOzN*&-ib_ z&(?0bfu+-n58=IKwh;@9E~@ICCDc`gVG?wR?XJtfxLXFh@mGTxawoKnJW|-2JODDW zSV_#O$>)6WrBe8^TE<Hld)D?ECxKVIA)*)>DyCkvWa~q(X)K#$&|FIRGKW2L)0@VlqBwxS%R(DI zRp3l$kT}7|81_SzZ%v_Pts}GO8<&t!1rONijAM7FwAz+Do(|=|^U9P*CT@kARlI0{ zB8CDVjqOXj^)QOpl{})k#Ez2<7o#<#yatUfweeJ|+B?kkgH))+pO`?aR25uzl?Q>`j7CjQ8<1NS( zeWIeTY(5O=(23GJE{fnPR)SNdK03jLHdJ)*Ar@=b!i6_6i6wm}&y9jMCF!kdpf;R$ z_x4H+{N&*8YQF2R+abIX>v@{U}FDV^GO}A0Qx5LkxgHfjK(vtvs^*gco znf@ant3I;l&Py@-wc*GmswvKg8h3RSZ4YTDHI;uqoc&AMbrDo0x8~bs#-W7rduW zC3-B7cU;HfK&GE)%E zjkDT2<*7@n3bzUM!=!AHo4if`0Be7qaanCW-p0EFcv^aiKCBzj}rWpL9H0E5~)nq7u$V;D}w8f$H({~s7xK2j_9&e|# zemdceV-q^gi<~~y7z5R)^78zT<9;kI^ii8ery z)StnYI$@t}>NV|9A$6w~iJdX+YD0u(Ad!8$dx zsSk4;p0_ujQa~ThiX|y*OtnX}K-Y>LTjU(pN`nP*eBIeP+DwMzP6b^6-%5uTf7uoroH6BxNv$ zB=@0M+u+1>UJn$#MYsq7H5@x+${4k7s(YpuT#&Fyh_;a}qVv7$CxDFD)*KUaU9Vky zT8gN$H!L+3pnDzi%!p?lB@u+@ioNP=;{oN;9CSTx#K+nxa~~?*U440m8HyX&ChQ_=-F zX74xxPFUWZG;6H{7jB@q9Q|TBil4|{oLBvXOz4?%;d9@~pyxg#=#z|kF;aSwhw~J8 z3s;(-NWONtM%%r#ex}MEr2C+{zgB>or-+~-@#^CW8Q>4*0KYd-9{VShnfby4ogKqa zMaD$#myej{GXMi-%|_>%Lw+i9p9hI@skikz^E;CG>T0DtGW1m+pSEMk{W6xX0nmwyoZf#4R?gurjr;IPT)pZpJWemV4vmTg}h zobv&n{z&1Zcvx&;nEea=J{1(tsp&_9RR&=wTC9f5I0^;Qw1UiBwlUHDj43fOYA8v( zNVIYM-Pp-T>IF3kl)K3Owz-7ZI1z~!kA=av{kg57UkQ=#$^@!;>`7>R!4pYw+fRyT zFq8yR&5iXP#Ro!H6S1f6`E$%jk^)y_+$hHinHnH$Qj^|NW+)OFYT(bzpdtfU*^@a$ z6yO`~rg1IK_juy5Mgmm%$Z9*6I;jPZ9n07s)WmXc{*b@V3YrBdPS_;Q>PIrWNYrwf zV@kBzAO+Xfw1&+Z_tk&QOgl~Yc(lu9FVWw9OFeej@`d_IeYFf!IGW+fr8QtxmmkZ3 zLE%j9NNtc(X^rDkp$O#RJ8~c`NQ@~tZqwc56sVLtqnW?{M?hA5wx;`Tq zwhNGBy>oZ*g>$dI@ld->8Ut(Hj_5_N~k<53j_o5wo z!(&&WbHqFBI$(b+QmrFyfviE&T;Zq}BH1VUf*DEFD$U;PRxt)&zyt6se7&S2tBv;( zEPMLf>Y@1`OeLFdi+@9ATvr-72h1Dm;#N;{mSk~Hi4;{a5w`2h8T_!uSBKOLJeX%f zYfT>JE68)nP%g*4u^`_3>dK}NQE|mFCizrJ?A)OFv4RLM&8-+x)#UA5eR=H8=2 zSHyVrXX+!k4HD!|>D$IaOLWAefvpVsln0C$o=!$$^SYW9n9-8+G4x4nFKiYCv#ywol+c*BpeL*bJ_V4FDnSA<6 zvYkSlY==r;zckp-Z*|As5W})A4kcm?FLef>IPoG9$;$t@8(99BRj@D*r%HOh@0O!BHn2`Y}X^z=X`%T zut;9cuda44uc#OBPP!JXeB1Bh)wbEPvzAD&yj%G6PJBwXj8EuBZa)b>8ST4woR4=@ zcQMX|VOSaAsRaWyg3e_jq=I)p;jk7gW#un_Dx11zB67FnnWSRH_eXTW2K!2}XXXeF90ltEeh-^kJ_Ui!lYC6)wr<5A%uFi1`@B2T=Na(9IZCPZ0=D% zS-~1-Eznk#5sjS6U+_dfWJba~ILNQ(@Cs2=g+#1oPol6ruT!!RW7!zMz>FzAUoX(N znT!|{`g+utMT0*`5&v1%r3NxUjs6Yq$=Hmo$NcQ8ns@ZN=)yDEHFo~aLzoR!|eXG=#;))YTzmz)*;GMq*WgYHh{ z7lDIZI<*Xc>O%O(CK(`l3;K4S7>lXjo)_LGNt;$Q1zvj@22Rt1e&;d)j{<&kFJSm* zS;zbK1*hD?UoPGwXbyiZT%t)l)ff_dh$<5^W8AFsVbO0ho{H`*-Yt9uYE* zH4vJ_@lE-Ok!9PylRV@)J1&?Tj5Y#WBpOI09N-xsywVgHB;AA%#=VnHD})4t<1GXP zCkMzgYO>xGWP)`97w<=q{C)zxO5m<;A?7pocqKgy+YGcw2nJUl_2h6 z#wy8sM-dHtx&1OO^Zg=rh=ngt)HsDW-czmc1FKi|n$~?m45EC8x2_53R#fNCP>-R# z$*-zY$uo|KS7)$FngBWjFUHx%C664#BU4s!PupHj6z z4DsIekTA*ZAv9okNxH3o)v~^lR|Oe!+R}Q&Lf~ZZ;v;Z4#E*Qnh=E|<<|6S^yN`P~ zO4#?oKJO!ArPv+NOYwabo5L_X2Dbpv^qyx+-0UCYXq_L4n|$NET5)GV)XvqGxN2$p z;Q>|}`=Ww5k3B5vF?1JE@DoVOb~?raHvMD@?rL_WXE3JYcV9FVys`s=ykF{yNBV5u z5_q9e12)RGJjgCY$Vr5mAADoB%Y70zsy(09+%U&~SJ*6EgrNy0nti6#Fz=S>Q?(Rb3~`E4**P!me{ByoJ30*Ul5)LU-xm?l16#F$|1rj3Xxx~0V> zC3@^-+U;!2E0OB3F&K>HtyBXI;O=!-K6O9(!dLY0t4~8G42ye5fQ34NMk?pDr|6_l z6fz#2M8duzq5P2;vHdy5K^o+V`_$Js%+F;iSTg&N0Ub^7`UuVsRHy1=4kkvMpQXz5 zax~-Y_625HBN*yy;txwKNGT;u3ruCSq{-q_#s=OQIo(oNz+qT|0%_B(-eZOZJ<{*b z(XB_Q4X}YlIYJDw<H>@AM1EeV=NVJ-~)Q8Jx8E;>JQ*SH)zfoyfIX~T7B zb`RO>Oqfbn2GOH|JxPjvA8155(GdsVp07flP~uvYb|_U#@E3=LxlZ@ygBJxo&l_wu zAnA_6LK2fNL?1dYmA+PcmrKD0hNeJYHGDYI;k}@b>>DR1<_!f3#h4r^fWO2lCRi+q zP|dFeC%nWP?-{Dm3)}w0(3{2fa6l(DmyKR`v@GFccQJW7waU2;2US1XjDZY8Sl$q` zmc!!w=A}*y^G7L;^{ddztyDrrkK+trv%H0WwKzXXp3hKd6FKW6Z1EdruN(26{rh>m z3%45WjyohIX9p@*d=Kd2DP%o4rwV4;Y~CW$3jp<|mkb%5H75Np<|&`;)HcEGVyMVR z_};8$%q!N$7hCJ4@Ep%0)x~fpZBsO*LZy|k6`V+-B?x^^B?%cKrq1t1N_+@*?Hi+IFn=zxJ&FnZGu&P%6zB zd9|qE70YX|?2UzIBSirF%Ybaq%?&Sy?8Y-YfmkLSZU-${ITx!uyc6-BI zd^J$i6exF46tZTvg6N;})%hVOxPl$ZpMm7 zIo6E8)KG#2zJI}fwDV*~DRTr#Bd#Vz4Vg4sIKa_aIq_X$#A z!jj^-R#b++QX&R6y~Rs&TE@hLi;NWAl_l=H9du5lSG@iSw8#}WB_@V0g{wtJw-OWp z)-$$K2IgQ-e@3=$q|TC!EF4&;fSi@hABf6L9;B_SRYhD*iB%?9V$V zo+A^ka6d>!!~#!kn{C`Sp1MYpr^p9MZ`t&Q`)R;-Ai(qB2> zJ1f148*6=AE|t)ih>_w4+Xkajdrn+0dzFSX2VTXxXfJsz9R>~F=>%~$zkKIU4-bXd zkx+Mer$St^^>^fjR5=kgNSGW?+Z((p4lL4yK>NMB(-j7`CXNbdJS7YfePl~6wQXOFY@<0H?WQAefu|V3aF5roGDu*(9(k;c zhm9?lLa6gngI&Jl9uA?I8XBPq<;Z^FscfRX6aEP$t#QwJKT^#z`L*B=2S$Tr7WWdv zI@d``cXM%cvKkH-mx6d`$KCwzfeaG^eQCQ>Y_2%2MvhZ_XKiXZ_*5ZZM*4+uZSQ^q z`w{*j{R^HR>jF2nlS=1_v3oeT&?)z8W~73w+Sa@~5F~oX=1+h?=s0XG>zOS!t9fl* z94aLi08Ld%%y(*)8$yF(A+h$+B5x8xa*DrQ`Xi|4L>yXYsC!x2sHe(iXZ3AIAlCMT zcd}pR2LACFYc^GMY&~}|H6-uexAf*YGz5^i<*|fJxQ%A@A-I+({^q0gw^q;C!Gqse zS*&bEmWq4q2at2GSq->PN-k7VAc?+&_a0qD<>{Aick>8++PrDMoWyyX>*Y8&w=F-; zvD)!H+#}*w zzi<%mNspJW@KDAb_Y}?hWC!VtK}b=BdTHMknTc?9!XRdk7kO3s1UERJo)O7zb%{&S zfx>ecPdlFOFOZ*rboQj{rzep3=CJ}OucNqRFJL~mb4xX59-++hmCbFVud>l$U3)AUk+lIjRqOwc=&$q@cL$3fq~#D{FgkwFngf20xDY2^R`G2=tLK z#HU$CsU=RhpqkpQR5E-hu+~>u!Qcq;#cGIAOEbYNT9bJ>pcYsr>{Jez4d&W-dNS}+ zO57>dLc^uE+xJJ4=P#$swE9x!03F490leD849$GVts#x#0Vns0xVPQrb2XmLm-GM` zH$e^U?r``u$3=V1!cN+_k+&H>yqMl6Ij*tvV&|!~T`UJlcY-k}V#AN?{VZ1dMO?T@o~Y?yWhx5@c}W%xR4x z>h`z?><@xjwW_ugoO``-is9AkoH4cb)Ofr1xnz9pwjST5h>GYFHg21MTtXZvCwd0R zh<~Kys4Qqza#^DK==Nq<_o}`OynB@Q@whxCop>}h+frcTA^InrE#z_j^Zv+d(D|=W zG1OqpNQ@u}z$S?;L>FQV68u&Gy7viMTE|V4HLHk>Apb8L?f1TX@X1yD$CHG#GhiGVlvNy?m zP<*H$qhr`0ke1bh?yOctnSTR0FZS!5*%!9l&FTH2hLWO*Tp9UgraRLthK%yUf-c18 z0;CE&!NHEsg4}YhUhj~}pd9?@I20y?Fc6t2y~pwjKPTW7DcRb1{Grf|Xy+ok2OBp_yu;#bCkHxv(ottuUnF#2V% zqKU`^1C(F913M4wPCIVJ+7aIVF3Yo1rS}tanhbQXXVk~btpWy@@TS}v4&6mNMh%U` zl2)s@C?UuwI)bnKpqxF>Slvs{h-X~%o;|f`_?cKH5$vtVzPVC=@zkB0%wwUBRTnm5|r(fkdWMpr1 zTU=3H+IKPXK%@9Xfv>MsFjIW&MU!!k#QCU{ytszG^kN!#Akd*(lKIXZY^p%KLQnwoXu>7Xty-~Vt zZiYug1B9ytPj>mpas>U+m6o5y@o-RW;N978I!-Rb{4cY@awU$wKFsAwfSF*DJ;3p8SU}a7GrLa5`5;(a% zbkf(olbw1zfsq3r_d;areJ1hf`KZ7gJaT!GJ%7JyqG?idw+ns{PNf9r~!)($YI>#KJRq!L^-g{BDp?SHW*ju~@ zRq=*L=9&Rgel2Ge=m@YqEZUomb_@;A*iv%9IwAiS7ITGF6KQ{M5e@Q(TXn$5sgb@y zk!=R;?k&}G;#OWlRE4g(uEve&v24m?)SM`{nTPvaQ~S}L*w^JXl$q=AeWp9-&?Ehv z;kccyofeGgr3ltR3*#~Fh8Aulsnf3H#>R_^id%d{spN5B*nM62zJ|2zHlcf}lwc;2 zk{R|jD0-(V{5Q?#f7h-2Z0rjbTd6IvN*KmwX0M|jj{Uk*>`wsymcgce%43OooJyRE z^igx8LJE4MqaM$BM@6+*4|mG58@6rLIV5HWKiVoOqGu_JXkd#=rMnlKVj`WC>%7R%eN7BCEJxg;fu-j0ayyr5lKzza=byo_`%NnjjK1?UP z6Mbpg=}DYsihV|Uo)#k1n?nCa{$3oq#)3cF_KoA70b+jLUerOvP|uGeUx{ceO_agg z+~GqWsRox9A>>>)@|>haI$z%_V!_->c|UYAPGnk^;WTgzFCef_05 zisF$!IVTAFFY~i=5Qtlf0AAq~zCiv#%?ZE_V z5)KZAV~>xn)r^p4-+HvOl~4^-dGRM80w4^#sQ9Ma?(Lo+H2V|lFR){7H9x-`aT3+6 zgu46N&%A%T;?e%%;gq4-17U@hKAP<|-UE}$HBd5W(f8sm)SyoGX$S>v8p!}fg1`Ox zGSxNq1c#V$?zbhXhBth5Z_;#dh@$V|J`Me9gIss))X#~72viBg_7m4?Zt}R(bbo_l zUyMt_7j+m z>)PuVq{j1u6vdJow}f*Prm7ckU{TJ$_)_CHBiySu(rzuBV^n|46j!TWGHkA(JW6tl z=)5YASG>$YXv2Q!$Kg7%3?+6{P=4n1UK9oA;#QYQ0x-Qi*S6j~eQ!@p-j@mg;`D$c z%nO3?BH!%`dkzMAlo&)XNX(XVUiXvDyVR7zhdq@Ld5NsO1+tleuE%e>ESwB{q&9W# zZHAflsES>Up!cV>S4$Lnz7OnTE3tc4W5b{mM~4Tn>&*^4hohG(Tqz*#E7u^POrd4v z*NY-+Y_D5t2e9a+8@7eF`Oll=E~1obzP6djLRQv_O=oPITFWZ!@39}t=Q}*Zxi4mk z44SmE!BzK<%f7(JyU7H?OUduD`eI;@ynJ;6BSJ|VCOU&g2H<-6b_Za+?eK8MWp!rZ ziJe*G>J(yvmis){xe&Oxm`Zbm8_v_nXpsOop-8n;EU9nMutmLGzn@A{)8o#b_TF(2 z3@e-(0r?;fMdcqQ*dOln-(r0{K1|{Jyn$lq=*}drXHiMC zG-6>3(f;A-oE;G)rqmyZp?Zd`ec zKk1`1S7IfunopcUMFpAo;07tF^8&SE{QkFV_7lI%Y@f9tHe2HFWFF~6*}1M;Lxf23 z{WO=_TQ@W0>0aWOS|&EP>oX8ztVlt0e7G;P`K#xCnu4J75y#sL2XkK0J!mfI=D{edU{l{ftxOZz|HqOZ3fKz{-ZD{FhgS zKY{-6`WqM38x-A*`@gwY4Yldd!Y2EhzDqK9{@Zo`yMWc7X#oFZEBU{wnfx_DR;&1{ zXad9h_9vLZKQ6$30JRJd2pCyDF>3ik|ET^G_*S%cq4FO}&VSo+_`C0a5iVJ8)ZX6M z2M|Jt{wE`z|Ki{Jz0UTZfRal=NW^zKR>dm6+cLvYVjp{B_SvxBDNQ`(R5V});=dc# zIkzMQyw3ooY=!EQcalFATFB?w?VDuTfV$DYUV~IsBG?>)&dXbYL=1!gm2u|zA4&$J z2O2F7JD^;pBN`SY#KhNp|7koz85F!ck7juJ_`0ZnM}?Y=7pII1d?x${8;2)|B^Mht zBJfOHbNbSv0Ud_*ledTCNX)A2{tH`Kf6giWos9PX_`aCj%It+`17?V8AIn=ppf&gl zc*cRR=RGdobAECW9sSE`5V_1B=9FvxS|@FgdVljKQZxT<0c;MOXS!9u3Y<}#{|QJY zHN?+&-pb?<`~;>qs4v+^&=+d{WPv04OIlQ8|BJc_@cbrJM*0`K?=D8*WVwQR_aCd| ze+X9p^WJjpc>^wp8%kRdqtJkAZ{$P#mS@cq zhi%^VUbt_nJ*eCHAUwogEo0%reI#aM8wn&Vo%vd5(sxvbX4vHl%)h zqC(P&h`mhT#kheFm6d{d5Dc5hoW8O zK*uwDN~v40ZtR*~&Q@A((|r&sZXaV*-sb|R5X!@katavr&NSr8X$SE`$di>QDvM6D zrAoZ!=YlA~m*QnT-umE&x0xj0{UBn z;9b8*3Zn9Prn3G`RDov(?X|;I|#)v(KU)rKFt?!q6h6 zu6TrwRD?7`e~dhJeN#T$ZSn)CiS5g&#zl&p0zkfyjvjB{)@Ffxiz4)I;uqH) z86h@{XuwG#A{kyR(8{!UH31A=sb>UiV&Y~{b_0fHJr{GO`JI70DTrK#h2|{( ze?7s!>gG#wRGlPYvis9_{zq$DCM}d!A)VAE{KSJVO z^FgfiA6wFf(tX$7h%YKOWR2d(Sm2FY%e65+%zq=H-0>`U8!3~>m?G-)4{J~-aw0E8 z6TCN<5oi2zPFwC6uk+fL<8ZAp=trazz0Z~AD|RfgJ_&_y{%t7$nvRf3K#C>EHljN( zTHfCW-E7dsBbf1}n`p<0%vAipL9TLcz3VqsUjJal{1LS;GCn`#EtgD(7hk}gulKsF zM`f*Q9nLR-y--dD6c)9zm@Q1FtP=+865|>&gBnJ*dN9zCFm6P)m zp!|b{4h3GQ0mgGJ^K+7DqX%XGL>HKN~32NLU$8}+S!cB^$ zI`;04+d)m`%r$aGz@S55>}#qDBrF20yG@KDRdGw5@-Db{SqMs##IEQ37cNSuFCj)&(&wDu11iaz2NG| zk@U{T#_U%ur}a+|ZE6e&4-afc2wWQaH3 zz{)_bBcZ2#e*`(D8oP?{GDfpOT=;!RZWrm3(2Za5-@S zf4xBHKtU3f(Z9GOcq$z{z^kOxXv_R}MXbL_V(n6Y)!-!wep5PtAZD+m0|#A>p*Q|= zKY@376|*A$ASeIrNXOqMW<*j>=j90P%U@|YGZO2spc@t@h&9Xlp>azX?))qLW+ z5}%P97oai7vZb$3`1sqE($g_2h&4XFI$GE+oXQV;xHWF6dJ)_}QExPY0xpl_^W7@^}D+xY%YqJta9X|spVl^XJ#R0CE)lsGU*wUs?o9Fwi; zc$Uw&PLHF2oV|b*qhQT4_a#aq1=wPc^+?XpS*VN>NWy)3pf3BVAgqeAwRpF3L2k4A zML!|%0}{vmfPx~RCZFq}K=MaxiMFn{e6F&=Dn{7u{3f=UVV=pK^;Lw#!H9up0230? z;gj4!ZJ2_=NQt~AZU38C2ZUACXaqvnPY`BB%2(#W z?6E=+tbRY(LfS3#t8<@hK+AZ(Rk-f3+P5BuVIighK&YxnLmsu+Gq?Vsgp>>KSh(KM z$!9@%xSS6+ejUJmGgcAQfW>emn|)Lh(fO_EUQUwR{n$^y~!rEKdjM=mO|O4RVL zzFGLb6~ri6g&jforC+QVd746V&d{ZyzRy2`RrroHiS0}{G!~GD)h})(p0ogZmsqhR z^35<8^m@XA+?{QrKs8|)y6d9++++Uxe(ON^!Uq(U{ilro_hp!o|Gn7pzY_cZPd)ZuGRNRgG*)cv z5I>D}r;}wwqPP1lOhgf(B??8#@2bT=zIg-+4#TNYs6;(r$gA(my_tH%1MCO= z<_#@8g9_i}h47vcjW1pYjylvTz10~z{E}km>VX&V6+)5B=p5vAIh}@()*JT|tw4< z(3f>31mSh5t1~UeuAXH2Myy;MwAgQCB~db6&p+H(KpdwJN+nM2GQ>$>rL!MatG@x-T?eF}1$RQ|?01uN6kbo{43+aeaQ~Q)JXB zF_FL`gz~{P&S$NS>ln0?4YY~JGz@3CW3YG=#oIFyG!u9c5Yar0L>FA^@Y;SK_r5d-D5`Nhn}0q`{}SymY-PLE@9K(suSxEjCYz=zw&%Xi zT*0wh=b#oA=Cv~5bd7QxS6UQn(Mh)FG|?isQ7{e(PZ@5V{yfZt{6WT_9AaMH5CU1J zjLiLBkraUJI3dA&Ek8Zs5VEU;R(DAL+>x9**5Bn-jb;C4+QtS^bJi=$F&S z11$$MvoE@qmXyk6wd>*-*bQVc0LnrBE`-xsdx)QLxsjKz)YS8HmU82~M2^B}9oa{j z_hRHF`f?GmtN!+VA`Bn0%nvZK79V=7YzdHT(_89x2cm$+t*)!_2gH{GAup?UT^GN& z>^3W;ss}TDPzL<#EBwsiTTwn{O|hRp@W9>&(%OqmuU7=08%y2gJh{P;uE3dl`+e3L7)99d@Z>Ipn{C~^4XGd*SM z&zUQ6a?eN5zv*XiStK!GTgp6eP3t_0S6mZ&c8uQDhKl*H&?Vx~xR;~nC4)=Ds#=1= zR?Dp^lAj`!F!xyIeY-Pto-$ zR`-$CjhbVrp8s$r)i(aL{y-Wp`HP?Ev0(S*V<=@AUwk;kY>IANM}IZQfsrUHEJnX! zFD4E&!Foj&_i8O?j;Y&+>6K%ht%h;QEM2i>g9Xx$(%|neC&Y4UZo32l%CK81QKeb zJ?nn{Yi7^v+3&ow*SGh>`kqN9GxvR6XE~1F(P@uCuk}mGRz>>~xQ>WlDcLiLk%=-6 zzZ4h?6u#!eIjtvtKr(qIJ;+;IP%`>9V_27uC8nC#I>E&A>0nhjq1*K8#l z(F*J_xGi+LUu_^mJP(u_ucV|nnH|TOhp`o4a*~xkRzUsK0lf<^>kF!n@62kPRe_)D z{1rBGX73)bWIU_sL9zF_kU_#PEKQmcc3#gfnwDhFZ4Y8A@M|jyAeG`PE?QW&47!HG z6%~2vJI}Z)!@?52s2`Y;%P)D^OUrxAH6dVEqA=L+RW2-az!9z09K29BT#O;;+mvQ) zzeN5_Kh8Lz7`a=NrsL-}8!VjQ=b>(_)Wkd#V(iYa5b6GUPPxgdM&~Z4 zYTQ=p))mSh{|S4^@x-@>{k+O%_@Tbsi}jr<)pm;%VSDY>vnE4#1rfh!rP+_DEHtq6 zeNZZd@}&UI!z5qB(XiPuBI>~j9kjcQ$8}|Yu*W`o|Hg4>l@#6S1*83O6;BBl*fW_jrE?AxuKf3PPF zf4>pZ9IP-t0Hs4vpl>YppJNs(oW5w;a}PaSoMahdgLYO?MHBcCbu47@w2Nly>@b+c zZTHAPZENxDU=)FJ4vSI|)w2%(=Rj*4zvSOkI?}0tDS^E%SYNk%J1zqpwfD`#PgJ*I`RL(YGQW)6a2hG7pIvB=&FVu^GMT*xZ(g z(%FOe65d8xyri4Cn*=+~G80B*E4GtRMZA=bZUSy1=uSbFk3NlIx!RX-#INNn!A zxFB<+TxVz2uo|!pNzBecPWLqZ1Bynl7mOE};CicTQYs(4uax}?oNh`-G1*Gk+FoXw z5~Xr`$*)3#I==LTW+{h66BM1c)xb4i>BD+f<)iyRAF(cF(d1lipKpCd4twXuBQ0m- zmgjKuzxy<5`qbeEj0=y<6J})iW()4ya8y}%irpEC9#)VpAg=KeQk-UYY)fFtp*W=H z!%DC=7nZ>~c+>to7CR%}8w_xDL_=6{k=n>W0khvy-6E+k>Htij+ui6~A6LkZ#TuqV zeOCM`x`_f}e7j5!e@AYk&u57J^K&RpKbhBn+NEL=D)rRRlF%wlWRf+wfY?YfHQD{x%6H{d6I#+UynUtM}&P z@p-Ve<(?h>uS343#@OlhG|t4*oi|1K9>xO}Go~TsOFOsiwYVPu3DE2M_1klX8ggA> zJdl+$Yz%kaSGZ4`JOAI1d4V(<;Z?p!m>I2{~rXS_~d9US*)6{xUY++UlM z7{VneJVPDUqBMI*;CZI|ldo?1Yq|7}FGkWhjV{2_1(2DYbbg)A1X`NV!F9-RHsMg$ zm~Z;5VCCFHB^E&c85EuV4~Xho6SZUtA(d~pLf)+$=XqIt^g1=kZLxD>y(IymX^(-L zVHIoLcz-OUi!488^B_{+1h~t81ma!P8e2^@D}Qt59@9xB>nF&r(`0q03~`VrFtB}+ zsSOWgg+pPuA=HCFo@unP!PRT!)~2T+@uhzb_1~$wgJo!Up$#L0$UR>X2i+o+P?P5LR*$LjGSI9q+f`u4k6P?bmDc`ZLK z7ZSTOcYh8+I&JMHZ)1>tAGDLZ;`33gVwBhrZs!!vG1T|I)r)QY#VMT##3naq2a{N8r zv84S)qC|?@ejb0B zy3U094LHlaakJ>cLJaetVA4yz{Ca8NXscqtLc83n12$T`4X*bUDqkXjwp*Ls8ds}t zM%WG3sn`|+77@}mwH1T$ogUc}y zCP{;b_-{3)9?u3V>`^!>w|`nPh4l#*$*3f2x*>xdpR2~u+NS-WCDPynLHw>!z!4d% zcz5Y1uCmwfL!K~lFv(=u-k0tdb@#7QF_yS8KGyvupa{CsnJdH`eGos;I1up~udO#X z6ZTO}sijDWk2?jD`k?mJzgy;*{h4vXyswnhHx1sX=>~fYIjnCcpu3p>&!e-v_B#aY z#sj9~S;?3^iKxP?b^L8PYQJQd(~|kvSc93$RwRgS$^(FRzBQ8c&N*1$Xc7|R(Lb>e z)?eb8`;V~mf2ui&RQ6+!O+)&DFI;uYqBy_I566sxezGrLgbc73OA$j~_OAo5%+`vB<>)4fgy}q?vD;^qPof zNxtrlLyUnv5}GD=p%c6#>`g+#|c_gO=kf86k3U-Sn6Ov5nnLee}6QA#;Y|-)9*eu>j@#U+f zUBOkN*&`5S1ozK%-GpZDGgx|weQON%u8@(Qg4aeUiYz-o-(XaW6vm%fyH)+Fw@iBG zP6SpEsKmxEMEhbpP$gwid}QRM7o+hXzkVW)R*#e%ciLi{Dft2C2P3vC@;q z_SGq7kd|^Ad>D7i=SioUOum_zPPvI~&W&mDcc8TMaA09&)~eBbtysDl-KaZMW#29ZBE zr~sE2Z86N_E3#9@j3Wl#W=PA5Ye`%*YK8umT1)Jb@r8H)yDM_ey; zdDd?-Or)v7Rj>y(;P9zwhIYXqfxi^F07UvtxFx!L!AW+D?_|Su$Vfm~45R?~Pj)S_ z7k!uEvnFb-zMmE~?=#Q@t4*$U0i@cY_WKzn6gKBdgxzFx-ugOVmJ~M_t9_o_O|H0; zw)*TDh2#QHYg6!mr)4UaVXu1c(k0UJN_T&i0dvuNwcoLnY+okPSZo$ zwVr1Hk?!v_>cy-RrBA4e1pXe@>di=0KOS6odOPPn{A++(v~NU53!rI37!kyC^7N?5 zr+dSz=TqpZpWg(yYcsja|Bf!@jOHjgYXS2n`zwC#DBBzDyE2;8z;XgnkxXs1@cRw|FU| zM$OnqkM!HXVD6Z|&(1?J7XvT|&K*i&`tyl|kb`y|SGWwj-P6kHm!D-~o!b<$6`jUyAiB$YcSNFcwk=GWQPM9Jy zO#YuAKyE>8M6si}z7waiGKh7YgAZ^MISyQtY>52Y*TB$~72;y{h`o9wJ>e5#qV~_7 zc)k)?wRC|3IFBF~NKbTa!!$LJW?4N8#(9;MF zUWe`qE+i*zWNxTjj~f>4<tjd)4I7zZ0?tsTq0-JcijdF?1w{6A+p>8CJ z7LJm{oL(_sXrG*I6ac)uLnny&Y^TyY3D*1iS={-VMvpd@sRfiX-X(mHJZ#-=T?654 z=KAs&o4*Ua)jd9RB~rwFJF8hB(hEwHRQ-rIfZISDh9XGF#o!sn4Ri84>hsIXYLq(& zlj!Zgav?(q-7Dp5qB`tUh+7r>Z}?_P z7GoW0>Zhz5!w^=iMIwe-vklAs=fd97($$ImRZEKWis}n%Yv(6Nv%zw?mK$rqjTBF{ z#8@vAh6Q-ik65DOD2d-OhsvV6p7pQt%pkPN)GBe##{wO`Vk8MF8HYJ+ddUl&WCQF3 zo#1tC%6%$aDMO8OEt_dXmin#D*qc0(Jtc{4Xx$&FJLU#Es=0~b6ILdwL_e?l1bjb% zlTU2L@h*(4QeK(SAWMAtq7+4O^iSrevK9Kry@N%gdE;8ag-ab0dq!zLmo;wV$|*rR zt_u6dJL=exetO(SwY|xyu7gP@oxlTQs$+WBMGqjvb*-BjSNCtbx5xe~%go*7byI|1 znlxW-ao&%T4fg0;T8tPEy^lIlsPXSp<+X@uObSHTLIV>9H2i0Ycb84zs;gu|?tFLb zHVnxR_1jZG>o(O6Js>oeFYzBBdKAl&Z{M6C!f!GfTt09J>F{}*43up6{V+xZ_Xcyc zchbSHEO-EiSM@D8h?DP3!I-N11}38#(&N7=0)v*Py#hiO9t{n zY4?1qq084C+HF3?-Y@Z?{UXz!X|6-_M}wAkv#!@djWLAV*5x925@G+jHY!jS z-ttQ0kWB<)$|X^Unt?8-lDhf<;jOA9)ist0j=lzZnLgYPMy(b4SXK19eu(Evxtxb@ zN-mA+pYZ5?1NMux>533sgE{6RzNzO9JOOniqE@z>9+fhxlQD@e3>`J={Qh4F%3 z@0BG-LMB(Rf<=IfN^^%PStX9M%_BT|Q{UZh{eevNBii7HO5kJ|8SYu%su->U<=7Lo zIC1-(BT0{KU$JUU=} z!>EdWJjzzu@Y?*lb;lLC4@P3jZ7cd-cYx%M#FpVPeBT_S*_GR{d%1;8S;;jwM#q2r z@+}XquWty^f_>btD1_6;Y#$A^t3}x?Al%+Ei9OL%;T^2ni_a8$!fBKRx}jK>Je(fi zU#!&fw{_XN`RtiEIjS{5gqGb&XHcM)!_$|6NZGxD^ZB zk2Po;p0=gDLP8o`DMCVw*&9nTq9h=I3zwp?hkTFP#DaHatT2u|!a6FU>32*1olc*P z@eY6OD5lK|KLjC5)mTjjS}r4awGpqP+#%!S+R@a=X2q^yF*kTKc=Q9Z2$!+poLS&# zsop$WD*@qee|`?cDT}eZjgn`t_Er4sRIMm$uHo$c>|T2Kry$Y~FC7yx(}VG3Xv3&$ zNBebZY8b>piq3E%^VLZOqHY;1&jy^9)t=fcK*2C$jkTgzPhS{2;Iya5d&l@b$yFdY z|2z!SU_k~KvUPl_eq}`LH2McIu|a?VLXPtzWK$AZ0>zHH{`xI-O1eG3&`lV48XG*kiFKl2;X@a*Nuxju20j>jUsfV$Nl{NWg7eSJ{OiP)<6-t#MBO`IPo!vdBrfJwFHA@_t$>79u zF!B$`qFVze@x{I!I-<=4_j&E+0&iaUQXG~tbvGf$r+|Vz5{N*g54pXc7}3r3QR{yB z1@+?dq6>S9_7=*?u+$~mv;Mz3@ZhMK8jy3rWxfM$;B}rt(0L(dIZ3M>`7%r=!W*AE zaA7!z3TE?iV&O!s$5B0;`sG#B#&H4ZKcFR(_hM>g%ND)1ps+~T#{}OUu1+aE+-G^l z4X+ojBJ6Mh z<+u;_w-mE3t4BHsdk^xo3dG7L!rP?RfrZ}XOSsb=|oMh&hvdq3CL6DWM4 z!va0jijiy?sB-htq`^Qvp^y%6!SwSvoED1Wb?+@tp0NudQ?X+}zO8q6Zpq2z@j#Lrw~6tl)7Yd`G&|bZ?&0Fz_e|1MZRyrfLXF zXo{@H0mHGpq{YOj+QNny3BgJ6SOyTA?>n&H$|&s9DeZ~NFf3dOMkDAlr_=Ga`1uE4 z>u2{DKvZ#I^E%*0__N_#7Hm_)3f%8lV3aB*lQWs;$rCVhQu0j9R;MA>rT>=Z<4-O3 z3?E(gEPivwVO6#9oRh4-W#^LLKLhMJbUe-TR4W6fp@a4V1~iWfOCar5(w@Z~oG*6H zbYAiC9@)8*_Mh*@#orr3`1Sah@x9Lg1cp2#R=ZzOUafgfSjS8y9R4DDn+TtW_LVv} z%YteXHmv`ay+hW!ki7dH=qI7l&YlO1_F69T8P@gy-v#Xw=EH^T_ty4jtK=corI?6W zHP}Lk^8IAGsoowUvBB3>nvkM3#o&wyp#VwZXiTV{6WmW7I2WR7R;EmZ@G z`0RvIftT^EF&F$5dhKu&pZp|A)m`9Zaiq7K3Kx%ETu`7M7WoP{F-9e)!Ea8LYr(7| z4p)~8QaVXWbiakrROQo*FF^%lj*mq-eMii6?!O~p1=AkW59Tl856XfEs~@9!A{zsc zEe#Cj=}u*G`Wd03*A`9V%eUWeg)lyGuY0?>zK@%)DkJ$V0-wCo^L!;+3!E$CZBr!R zEUnEP%Fo@8!Zt?YIt4beAtdmT7k0?ktuc#yzAf+VAGzNNn_)U-kA*P_%kwHkhBuON zdek~n`JRvQ{G5tXJ;|{wlg|QON;gw3tY0#fxK-Gnr>ePl0XvoP+!$c6!0HMyGyHbT zjQfUOqrkPV^+RdiYgM(B@-*jZgk{=f2CHKVHsX%8;D#g9_Ym4mxFZxzDM@(ZrT5ZX zt`9lBj$wIA+TwVZRx-mfqobFvt~cs-7P~#L&|y)`PTvAO)7LA5I=}BjEW(3S+iGdd z!Q4mWhUt^o4YXS5+h(jtqNi%p_oMo)Je2Vp_11THLSMS`;~%UWx9PlmfKM|+6Z4lH zaOJV|kamJg;5({`S5F+i*s8s zMl^hfp|(<<3Jo9;sDJMVRqhTv?-U%HaXEQZ#30x88^7Vdxy}DS^1UMN#K62$wzPYh zbX$>^L+l(E1z<*QP~Dq5X8=%Qs&2v4Nw?Ts_L8hL%6jmXKpI;aIOgxjMs1?kityfC z-zvmL%ivhr#zm;PdDy)cnGm+nD#8eN84Mz7f>1hU(O$<>7J1jL^%iiz>9-$_@Bt~z zOYZWzYTILb(MUIG_FNs{CjvB~zmPNLmbGbIJ;xHq$RQV2c-bIRVuU=+dOv{qFb*gz zi64GG$h+XFb2&_kaJg2))*x%oMvh3&g z!?9yrOHoAe)~Y2g$2mE)cH7Ouu>zNB5k&o4pzNvDjLWQd)|=jkgs{}DJ$J{!K#qOI z2e@RcA~aG~pp!|Z`lC<%%PL~Q8cQpFw_go8GvI}0!^^;uyZPE}G24>Azqh&uQK_7r zMPx!2C73@Qyc=aMag9x#u|f`=x0iNd!q7*NounDF7Iv!-+m4-1js?kx37gBO++p1U zhJ*B3z!)e$hGM;^sVgDTi9F;ZV(+;3AaZGh57^@$FJ6uvJ{Bq&^iU=8;U;3ZjA>*L z17fT87rVtV%@t@^B=nxJJdKa*Wlfs*jOkJa=e@=dy&%{fWD@++=lSFv41_fj#cCXxV59uQ)8fh``29YU&#ao z{$n4sTlh~T2*~UA_d!i8*T=U>cKB0&5QIf`in95`$u5rLmCqc0a>U17 z-8`Q6Ln5M*Thlbf?SG!+{D?y=bsQ|=UhyyH8KHn(<7Aa0vz@mgQ^^8(VrAxFayO~3 z54(SySFJF#0Ty0WbttYNY+rrqpWHHPNX8WE!@_S~#IsWpAXRw|924%+GSNB8q%GSc^Za zr%tWnO$EMDdwwY?Z5ac7Ktw+q9wDB*r5lm-CSQji|J$_-CgVBQzA=4KHFR%5A$hEd zc7`SeAEZ91jbi_>Xd8aWbl_s!+A0vTJ>KP!lkj6tbb#fBF%g-&z2Y>T-bR`Xy9r1+ z0e3G!<-;%@B%$f#w&MzF*IrttaGUAxR;K3GF`;gK9QK&nXr1zJZDABRFetIMOH4iG z9eJ4aFq9O8x~5(4;zsR6%&FEv1Y=t5naPh%sKtH~oYl^dP6OwA>oWw_T4P7i=i@iCZl~yOdQ7M5KcIo<*?|C|$%1+% zg!I>T_kq7MwZv>t?c4DHS;JhT>&KX(jF{}@lyEDR z-QNvPeQp@HwjXzXmvA#OllV1~I}e?WA?tnCdrMN^I#1(WQPE%Rx07P^^k*LjTV$8_ zEf0`AT1yWz+Oyzpp!e8qD48JF6#iy;Ro%y`us@@3{(3xV3wRs`B-q*ZE3s@)f`A0= zuK~Hvou~2L=f@qIJFIes3fPT}?tIMpPn`Q08gfu=_dLCPld4;xY==4I_Ti<@5Yda6 zK6Yl01DN!+(oK3d9Rk>XCdOZDmyP&T?beH@pQI<<^Cl(f{|XKVBG^3g@eyRejEXst z&Vgw{s+$W5*h1D^Q+(|`X&1$(C%#nY2)jNARNMuKB8Jt!Z<(-9TIZZQztJroFIjOi ze%G*kup0~-tx0@-lhH|2;t*!opsdk5?79=T(_vT$lq4cyw_Lm5?YlWzOf!NdM|dO1 zHstRp0HLGM1Vwy|P00F{oZj~5aBnk@gkt1Ex(PBW@ZPofcTT#{ zJ($?5-Fr@)`h2eImaGm(5kWRCk&5&`3KnXFUZK&y#)$i?8+dD)+{pVq^EUB8n3zXVslOpUR?{} zs!^4R;nNbQ`z{`(5zWUxzqCD4E;>xjvFWPqUnQB5=PVKqBXDRQExfZChnN3K+wYj{ z=vPoLz2O{e$&@d{{m)dG@3_o3%V7aT;U??A3wGBX{@uLq#$U!1J?LJ5*J%!8-TL~RCk!1O9k`Hz!&abD)6hNX*SDwAVC1UH(2hblK9i8l5Skm|T zsVbk>&;~77u_QkC8Or7B?I_&7Y{Q z+PHGhpe99dusg_G(E=ZD=pD}R+y_G!UJIdC_^epS>`ZOl_lVkfMRs8j7cxutbh-fMlDRf~pIc6Hm{ii28TUk?!3-tR_< z{>prIpE3Ckq~Nyz{!rlEnQNayO<)FocPY9clabLVdB6*>b;9|EVKI5EjW`us4@>uq z*A_zMMxU*H?&yFYQo^Stegsb4(g#sqKbjSE@hQ5!N{GzNImxf~X!rS3yUq&L)vT=( zOjfy|7k&hO>Em5nvOqjb7_eX~S@r9H%JM?74@6vR5jk5CrmR{v{5G#>2&9+kT z_|YK*le}vIExsMkBbcp4cR2E<%XveXwuWcRICgv-K&Q~q(3?Pp-(h;>!rmG^fj+hq z&TTs-ekEe!@plC^>%dCd{~4EqPIvRG^}W;YI9qcJ;xWIjfi#)2+Q&rIA3ccM4p!96 zAApjV;v#uhi^Q8RW`|??ZB)$L81^S!yQv!+F+C&x9oodJn!eWM%QCI6`7&8dx#g7K z!gU;XVJz2_7~2G|$2Dsvl@5gZh*?usdK zFtA-=8(@3c-y>qS-L*E959*{v=el)jZSr@eKzY{$HR4j#uYMkxYlkY*!L@st5Y6or z?Y(pSkkAono`hmR+^A$ru6!mP+C)Dm`cmF<{@_sOu9^<>DSYv-_i#bE*sMk z7GPW;LIGV(W}2zDDsUtT`OPIBJBOlBa zvNdfo8UC4D50El!{88hSJQ-IZ4nsJ3%7Bjmi-0d>ZN>f?#XF>Pk*Q;B>${H+E1qWe z@=(nKva+LJHB-Y^D9W!bZH~s)JsNr&yqb5H*#!O=pJr>0gTE%SJ$nOWDm~x4=Ej`# z-`@LvC=V@OBo;g*4=*Agx&JzSGsU+6k}A%AXIJ*&+A7=!M%S*~`nZpGMlH&nR+s3` z$-KVH&$a$&l%vbt<=$h7#>M!}JnL_xyfHapb8Kk`?HPZN>sp$x+j52Im>ru}JQ#%$ zvx-M@t$n98siv5a@A8Cvk)7OB&96BbVA=5<{;3B~*op{mB8m~e*(V;Iw2P5S23*Z& zbXzW(J@3nYG@17w3L4U_sb0*Ccg6po*B={%R9wElxFK#oGz3*J)kSYz$YTnK7-@hY`6$mWH;o;n+T)K!uHfjQffPvj8|hLER-K? zx|6;KvH_VWlP2hb=x5XG;;uaTzo@ zqaz|A`3|6S7XrJ$yBL=b8*`0hpAH2D2UC3ezK`;xyH9k!0W>YFYj6)!jKn6ANHg|{ z<=XlC$C5^ORb}S`8FAXEd@`K#PXGDCIwOlA9f!Lj%z@7kO) z8I$Q1M7N?rK=)PIDr>hnEJam6C+@^_4Ksd=AG-Bwc=Bx>_1|Inw!fdY5;SeIjyjz% zv3+^sxWGUrhjRh9!bV4CW#iz*DhBdUi0=LqcA4Lo)ny~oG|(8AGdX{4(p`6E&*%~= zE#3)=q&|&@dSS?}0NNT{PhU4}1X8=x|3mXatZGW>ny^Cd%Rj+gj#KZU)-I8LY`HFF z_TKiiRl@ZT@9dCwL4*!0DrylTn3iaH5&DRxsPyecLN67$2&tUB4@@B3z(#H4H6LcH zT2TaUsn`1BXfuo1&Bn%frer?<@n6jq52U;$%zm|9e1a-t}uUWkJyM8dFIaHfDLXPSQ zJ44{iqHV;rM8N>s<<1_3NVL&QW0{i(x&9p3Th4(pl;a5M#Qf_p_th=4Iqx9YQ@1S8BUH zWu;IE!Omh?3H9UJ>3Prp?dkrX>gVQYuaDJfN}n=VtdH{=b_~TpLp@#aRle9?27RJ` z)#2YpskZh8tOG9bqf!yBQF}02@Hx#&f#DEp0s=foqfH9JCoftBzpoB{50D}^%w0h+ zVB+Vk)b|X(ui`*@vn%a#;1rfkm0T z?s7N{#nMZ)`Pa>H{C~Rm@%uZuF+LiY^JWQw@$u}AlBi*_@oiOlj)bBk^4Fb)m}lx( z$|kpJQ?p5Xo+4z&i3HiIE=cMUsDQR!Q}#6Snw9^w3LF_+sK=;paM5i2|fq)Tr(!a7_4obaN{pQS<*bK;{W)2j~n z%sUxpslt&uGtb)MT;*h6$e7#6?8o?(z>j!ScI`I#r~roD1-h~H^DmVQ@v49D*BGku z+2Bmbkz4ivh)!ENnf+0ygZ z{cY71Db6cmb!-I!=B#@nzpnQ2{`jIvQB*mYFTMCZ33xCoSI&+|;X#901h=&F>oi6a z5n>;bNK;d1c3P@IaJGs7%)1Sg{HxEVYHys1jyH6PbG0*$$-PI)s@?tp)l_k$9i;;;&?tOC<}vMv>j$WBo~%qiO@Eoa7gHr;9a07Jq?+6kV!#pCghk!S zs7hU1hiRU0Jj)Yx_mOSBiUJ7tO_{tYnw{*L`$tnEsVRx@=V;Q>Vdod+iaMRA_d9!f z^FTO}Ak0>iY3!%Yt2Fx>`H|QO<3)ujB2`t0(n9yxyyv8xX?=k3HBnUXBadmBgVWla zt(%XEtV%m7w(p~z+#Y1918fl=xqyw_mEZVN0Hs>U`{r67oe2I(!aQK$_`ccwD-Y@- zgEM3aIiugRIGUdLq9x=j?LmfTWDT$L*_m4CH9o56&Tq``JjP&?CwK-r#`R+A|JGjR z@Ez1h1j2Pj1#S8kp7e*G#JW$FH*lAg0_0$^PX|m=K4cuL-!Xb(Be+vTBsKi)4 zZNpv+IvJt^8$(U~L)O(+A+*+41P0sjasIF4wCx{?+dNJlAg4-F zssPfK0 zIgc5noHfAZTr~VphOv(0TGL2>%$|C3SFaf@_R^88 zy<_%lKRx5ib{wZ?UDwnmw#y=ymHJ>4U0rn{Ut&zcnedYIYP<9Gvzu7J zo#9fh{n_4aiB%PKXgi?pGxiV2w=M8&Kad0mMfDJxOxu3`vQ#{(Ti3ctt?dpNLYig~ zx~LnA;~mX8=Y>5xv&t{BJ?Ud&`d?o{nh*!;)&A?iOl*)0$bk zuUj)bQjZg)$-zXyiZHD9Ipa-HqbLTYy{I)by)*$ominvaEZ@E2hipW6;{JZVhDrwB zZRya)sW(Y~jPeD-{LVbmpydRtGP@rngr2(q_Khx$Ej89W!cQRC!32^o{df5);-Mf z`&PL7*`U~#XfN&P6kn5}rqfF21bw|CVVGSiALI1%EC0TKKtSiYZYm?#C?{&~jB4Sx zks;S)E54A}>3(>Vdksb_xFHM)>z@U7jAjU{pFk#u|9E79Fsy7s?EU%7^e=E8O^w3| zYZG_hjGV|GK|6Y<{E%zmE!Vx43YDXZEQq@pwE=scu9@mtxoCW6S6EK&3W>sy->r{F z&Q8~VPm&nnsR$1|&B50$AQU7)MXo5G;yvA_Xl|j4LQIP>K3*f&i@{x;`~0ZB=B?PX zpupbyW`94g+s>9#%D2Xpp!soMZG+Rsm3(|gZ&Et+wZ*dyXeb*5>s%+<;bm9t<4$Xp zAvE?SoCQCDAAe_!Y*hX&Hrw1zKOx`6*E3#_yOyAWTui32r-81m6mF?HFE5AIOtlZP zm-sRD%ZNiXQ>+IbmLjlw1iFS|dIlW>Bp=*!vfK8_xu&5$i4`SG-arE&X z6E2HWKBnH`ClVdNd&2e@ll2!Eb+(Mi4mG}fU)24yJ{&c`?H(sa;b(pn&4o)_XASsV zpY`LkLO7TRDwO{R+w!sv@DH5T65zN8dLZH-zMpEor6$yOCqpdxdGRf@!v)20PTQfg zk75-M26%Q{fyUkhM}oyyW+yDjJNB&aY>plua5s>Itu`yZau*oxq$pb34cyysoz9(7sZ@uX`a8w5N@3!D;z?EgN^5QZqPnv zhZ_?6m*8+u80GI7JK2D*5yQXeq&L2CJS4|UoxX=RATqc_jNoODYU(g{Iw(m)EhZm$ zEkY)gWG#9!btRZN= zmtyWcYMN*{uUoF#+q_9GQnb_Q++i4o*K*(zH(1sB+oi((q-v(zo7-&V=MIkF`l_z* zc%etD>ir+jSccPm_1>6AAW+@_?KCdC_Yxe}J#bTPcVE^+P9 zg)`OoQJXpjaipT!upi)ndLr#<{?-Fnt_p?$SJekXf`xUV%&tAW?Zh*F599D&cSN}N zy8F%C`IiU#Ka0Q0zdy;#35sJo#oNB+!ufpOvk>`yZSQV=*jQm=)SRTMt^*(T_m)`m zb$qZw1D!l!$-5ac($goVPZT-4vdPN_;w)P?Z?r(ZK)Vk)b*!b+MqdaE^NQBrfAZ>k zFo>H~`m9#2>n6Wbg0jgUO+mF)m;E|j<8XnFs&f1RPYSK?o*#0?g<4}PgYh_F>s*54 zua6~e3iv+0uv-YuwqKE{9Wz<~_dH`)@Kprh0}x!6dT*GoeI@%l-Cdj9@Iu!<Ea=2mzZV(thwZ`kZB`Zv#6(Zz&~VRNO^*2e@CD(8P? zr~JrQ5bv*#ySHHq?rMEC4Sl11Wiw?^*W*oO{LYQ)&6j(KUaJk({a<>74+p-)7wu{t z&(}=2Jz+D(?4u0QFgL=7Elme2gW1==K0O~(=RkcWo^qV6W?ft<5r|fGWpF!4^lSV= z^<0G1FUCKWld$a}kWr6E_2m1A8#fg{_+eQ>KxHn=pm>g(YLD zPgcj@j;FLOEz%f{L#VJ>3DPNiVycfOH28qIf*{a06UCw&)ejmeOl}428a_xdtmow2 zNjN)oPA(fLvHQBK+gSe*@CZh_@q$<#Jt^8o7}w{1WGa!mQh<0Oo{5YZej5RKP4cR- zXx!~5*ZT-^TT3=uixyRktl+khUVtW1LKGZJSXExlH=fNG*_AM-wQ>b3KDtRd`uMvW zN*u=1obr;~PHl+;LLNu5E)uaAK3rS6_}OAbefArN$cfw?hV?p@?e;|4Ye~#bY?A_1 ze_@?Iv1D1zOk_hX-;q|> z|E;cEM;A}JzSVvqz638(dC_uN7cwN09^*>S2&_6{L=LK~Z;BW8_7pB8FWl})6Ic!R z#J@#YH_vV1)=E~ERboS|3SGe~CopVn4G+!>HG)mN=B<8DS-36mx6P&V3xG(J)nntu zS)zjO!V@6Wdvu|WI4&x|wzuKrrVLI z!pdb4+M89=;3ZF|QMTsmY*ytE*Ter#6>m4@h5;KikVp;(II`OFP37s1@nUb~$$cnN z_+Miv9QTB0~j8yuNN7ZH`H3bb@?c8|Y@Ag**jbThPgg zq3>O#{qfqOr4iOtXr7osK>Mcmc+geF8KH?&Szn88zCSYLEf7SqR}64j?z*73+kxUO zO1?7;KrE-u2J+HOnFRIX`< z$JS2fl=j>cMt&{ogK|R&J)O}gQ|+*fO2>1}26OC$Z&dr{#a@YB%Y1hhOc&s}yF2lM zECV=2f@0eWAUTRsaDP?IPN@6?RC(HwlTdZzkNC2|4-fBtVK|*445&jhh}YtjgsYD> zpuchd-eaqi(!0E$LZ7-`>nF1HTymT_Nl_~$5(orp7@)v_!7yA1;TxrMHab30sx7uFQD z=a`j{cP{l54gV>ApTj^5)XQ@=sp4@Zj8HIkUR@Yc_BqZh)q!ohd*mF}x?)ztO5u(# z%(L2R;&rLHeK+&$smAQNe|8ZAX}>CA zM%;nSK1>}x($`L(aB5k!Uq@C>Cg8-Am?^_bd}ILwmTSRmBr{EwPVlAA)An5B47|rx zksLRFbimVWe2r`nTs-Dt5V>vlhhw(Hv|0Xrx-uzf3dE?e-*HnUuUovQy`Bj9of7}l z8!2S`LHW798C6?zYDnrrcOrHz6d{2HYdF!EggZN}IqRjfyMzZXU_p^sE=+edDu{U6T5$Tykm3%Nv-*TU$B|nL^rhpF zpoAubC~%fu)}~_nnWI+`$$}4B;dts%_7supVwy!OQfmHzH<7V=#BrZWHadXgd8^5HXK zx0$nwYxW8y6f}PBw`?VuSC(AntbL&VPvhWm7I!y`lXa1>RTd0Uk=K{qE0rI+s+q$_ zpGJ~8sntL~^4Snvn8-oq#-1N<4cnBdB6(YNG(8D?#PLq1W1ASRU9d|p8fJ{d1KGNp z+VhmJY8my{#dZ`N*G}`JNUHYRBjRn#GzouH)eoj>QvO`B@maDoO)@DLygn&81nkN^RKQ#b@Ew9w2x z=XUp;>F#^(?fFg5@6KBH4_57}4Xdcy?|$A#KVS7`oiK^m0xQ!U&{hz0RMNSxClNM~ z8!o2gX@rrf&vYo%!`s!9Tx(xqSScluS+$Als{0K}%qiaTLmq$kPq7^ck*Tq<%@luS zRG!3Gb|fyjD6?d^OqN67!Z+c`x0Fkew@J66M9%ne6&p^~SB#r;>W(>SDtyNIM1iQI zn%>{*D%| zGJ5}G1rIuZb;qelOm=2Y!lU%Vn;9GVd3#-Lz@cyfQ3{ONeh{ctnN7 zzaj1a6>b0D|5zC(?$hT|Icjee#6fdgB1)LCObUD$o>>|XD zl#rv14Lmuu_&hltJes{3bk-RBsb z3HR}TY%c?vgZ~;x{dai|SEa_1eQ>?4IRjS4h?$U5LCU{x|(^^(Dd z{bibqGfzkXNN48?TqouTuW2ut%?)f20#Pt3G$of*AS>NPD^EopRc(opuolh_fDD}! zura8^g`)lF+6VFK#qahF+(fj5UaMkUj~h-!G9n6JKNsAYD+t!&^BdrNPq#0la6M_9 z76MA{Au1X=tE476RT;P!98hhhI!>M>!9RND1}*~87YI>#7eq$vPtu>We^1bcx81ulEEF%aZ&4p5IKKf~e0EtEhqzQorSn;@+J)XqD z^-2pKrlnGPt=dRtd4o_*s#Ny*WCn3@p{4hO8-=?0EDxkTrI?q;gyN$+`y73Fi;1{E z-rAwi;U)R<#MEo_R6!r9!HYdv9HX7&V`Z+c?Mjrq;Ihb2eMw`jqj4bE7X?^il4Cw1 zQvx5NwdgM@mUSAAKhy&h;_5@z5PxQ#F+{ZPKR`P4|^O9rYE-JV3HP3Q> z0svc@vT$Y=@(^7vy`i((982E<#UxgWHcS3a-z04cTqZF~HBi9estC|^r<;{k@E1hC zHij6Di)eu1JhWu^whFCT+E@*0-SMCg5<;%rEle9Cu_WLnL(T+K27DD!R)Ykgbo92- zMn_VnP{AqC>Ixjy^yw%@IPQMm$~xxSdS5{aKd_oYB7fp=VWK>D*JA}U*Y z9DV6x66l$#=Qp|K^P0d9XCi73e(ed=UMtO%(zC}a!xUO>Hj&7TkDT#~>V43fCD;@Q zZ&R=nhN_5Tl}})@7{c%K2$}5`cfAYTrddZkDbe-{lU{HIqvXmo(|o`RyN!4*cISj} znvG3FMIwQ+<|}{bfgnVW>=04Z8KQfb>}LW>e>_x1H=fL;yv@o_Hl)){h-Qn zzN1yftxo5O?UD<3z<<=wKqB_>R$@rGB2>B29#k^(*e;}KaktHq#g1SJx99R5$9epL zlbWyRl0@K0;e}XL{v#&rA9rjB3JDvW1guhLPoe^;KfVgr84A(%mc`AO6d52MwalUc z!oj~>r^)zxlbLL4@c!)oL_Ys#Gmv;g5epFy>QzbL6##rTrQ&*V1t_PaVR_zcmfWB> z4D!;wSFhNwgL;o-YuI(RkmKB{tYxvS39IcL z9;KVrPs(GbRuONa7&SAg2AFQ8BLG-W(p2vp2eS~p?DgJc>{c7)iy8$;UbyLOK1L>^ z0(BBd*N9%;MG-wgjIQU9(JaPhKe>pJ(&{u?aScADHeDJ;M`0fJPMFA7Vv6iIjc z71B&4f|iDr&J* z1+-y-Um%?V50;jtxe!YgPZMo#G~uLFSD;jUk6kZKS**dg;B2x&<&>X1AQ? zXIyRTc1oZlbnGnr1!zXaeJ0z%vq9)pOiGUxWsrOPRRmkPjnU?X(Eqx{{_*<%c3i@< z{wb$mm^?aE8}UiB1A;2?&eKQ~2T9f5?Sf$eOz))hLpKc6pV~;6i}zb!!W3CwQg_pj zhM{t34~WHY(G_9;1PVXfk@)B*j3Z(jtaZZaL!$-y=v4s%&L2E%(}87A?t}f-kcNMy ztiaw7g#AFmu;URCS7`qdZi*O*_${5z7g2o3#T=?;m!BTJ1kD2IvIvi0be(d10aRD% zR6w4%_jruT798v=iv@ZXQ>-2Jd?EV9Zk3j1P}O=I$em11bAiioaiG$lGN$}dAfk_X+dVUWCzu6pclUTYMuvL zQ#r0rEG-3xm$CXzKwX1PFC_VB$Zc+=U?K@9^-fY*%p5kY`Nnv23{5wzFh+_x)q5(! za-ZEPQ8eM$OzWVlcs72`v`WES*}l<@0=LmYOSo9%7Enm${<=r7lhH>o8!7RL zlS~Ol8wKTj+BxY@6(+yz^(0;81KWhAe#SVbq!Exb=#Um`p7USima#!XF7``Fkj5pC z8D~SPef_~NCG;^2na+9g_|*&plH^*3otfW$`D`o9{dkTTq@#jvoh?ob#9csn;lJxg z-{;lh_SXSxO-;$vJN@ja`k*YelYzIb1!MhQ{rityj_`+j?A~O}Ork!(EBN}bh*g2s z9X#Tp9I+4N$7wXzK%IJBYY2x7>QGbfEaH_nr}9Z;@p&ya8!J4#=zw*Ijt;`R)@i6Q z>CUYFt$JD6gO**p+SzwV9GFg%<7aoU{{U(Pg|Wj*co4WYc5KrunoH~ha9)T8Y|uHI zFNPFz%JQIYUysnB0T}V$R;JawxBnGi_`ljjic1V&FulIJ=vjt@f zHYDi1XK%<4_*gLX`69Ec%6W@+e@sffkvD*vt@3q7OOj!?z@WOv)6Egy%ZQAm#R<=Y zn9Z0fpSWM#N<~O%k&%y#iXyFap`EmTmK;-F4{Plo!_;vGa**S?>n{TgM$>Z1fo|yz z8B&NWJb=4d3$!#QL^Sw;{8=rb{e2Wo?SWSOO?-mC7PhGcyz+;;Fc}YXie(T;1Z^y9 z#_(nML^$W%ox!?zcPn$^!e<>1bnVh-auyd94qNX_JWxQI;fKmA<=}v_V_B?!*?bX) z%SLc6#_*`#%B$SC@4zasnNyA8wW}_w*Bp~DNQMBVif}7z6$lfA{(gGI@`*6A~H}9MAoL6V&p>_(ZtR)c7l@xEmCa+<4okV%W?s z%A97VpsKH7fl7OZC4XZ|N{Q>xhN{bG7}MxYYW#sgZf^uZxIob7F9xtCSLX_op)|Js zNi}oa3n|>fxmV%StJrtITngHV|Jn#4j>~6pmZo3@Vj}dDm(kHG6E2`Lpsq|~I8;L6 zkK@1XTLBC-v^%(Mird*dtJ$eOv|wFJqk-EFJqXPDHzeg`xR6fvz!ZpgJ@BP8K~}h4 z0oy?9L5U0ox5I6D>CPAE^kS&P*IsTUbzg( zB2Id#>Ek5{Z5va!fLE;-`P`~~7wVsFO0-SyaX*xrGqjQSu#rR<1Nr{~A#ryQa2Zlo zMGJr2Zv2F<*pOqBN?)GL)&q=>@2>Lr%THlbpZwS%fgShY#fEd7TqQx2VPyRSwrS?4 zw44UmX>^&G>DrN~Auvmewp1tsV%Z>h{Ff78eECV`BZ_C%&NJniJ}O|%7cc(X9G~AD zDnovtCq)A$5FE1gHW1$={1nWukTSmmI!vs(4vk|_w9AfK#Ht=9S3oB%DP+;dhun;*jxb~j`xH4T{(b3#X1LCp7QT}u;hlM{ zdF2-kE!~2kk*5?-aZ*)=!oN4%%EG}WFwNX`Hb1AYLt3-zAG2Hzpe>h%9!i1Clr_4My#h5jw;^AGm=pO^N&|83VuTO6m`%0jE{yt?RBCoeXk z8*4q9iFa6Xha)QqlNPc4EHlFy(6o0taVS?`1nyf1PeJq)CG%njZ5+oT&nZ_q@dbsh zxy^p4|GB6l)nWT!LbP~b(f&3_O9}Q^N42)!te4Gy&0kYl$0YD+PA=M}Fb5l+($qdPFEb`VU{#etNfmxI9qE{ZVP3-q)| z%?L`iQWOW?FVxUDmN{D=X?8%F8^qQ_PTce#m8brXE)jC-+i!{>q^-RH4kF4!trw0$ zFSHSl#FtmnA)R7lK=As{0QPT`IR93V{LcXPKMD{2Jpu1O1K9r!CGtNFU}yifm3Bp2 zsxT!Nmw^_l2UjYJO}Ma?A`~c@SF~^E`W|8xqp%QzUTy02LE82-v}Jp@bN+M2%-^~t z{%@TBhm@}S2iw&D+D<}={|i)Wl3Dg!Y57m183g$8El@=s@q;Pf&X(M$;)HkY{Q|+( z-G711vVMW&NUl--^gJ6SdA%^_PZ4CZT3WJs@B~7Atxx7TbKyxz@Hyjf8S$_^U{^K|Jmm*=e{v%+XeKi)O z_=m#C?>qTF@N?vMe*u4eM~|3o*f9KE?&DvPcSyhH{IP*Uxw zDBDL0rF{Wj*X7Bfwu%b^^ao>iazweTSJhgQ=%p2 z-wmOw!#TBTrDz~M5$(rXB?$WRbdpE@niO@7M>OIKE2c!D;TvTsfpSB2uZ9s8ENpD= zX(5ES2uxm^dRT0}6kxu6$?rBt)oP*>3`=ETo@q@{7aik%3rIW~GST5mB$u7vMVZ&U z-Na}|*1XMYe|x6|B@EJ~Mp7h_BAT5=Te7DpcC9!qvUpU=D`I0Hmv{yIJU;$ng@AHZ zLVW?H-WSw_5)N-hg58~6S(YNP3@CI`LBBu&UEr6O(XjeXfE^xzbN*cY7;CSNOH}ad z2l)eU1R0lvFYNR@;z$4w_Ey-dN8psIs*_|3j3dLaw@T0|iuAD6r+^%2F5ewFP|^jY<`LZ{oupFTiluq^v$snMX+234*s@NW|82miL;iR1`?cG{iHIr}&@^>QUupU92*suWo)EblVFNj) zC^pcv9+Z4h^BhY_R20JLJ@i%VMjPdY{fi8W<(RJJ%T#BJd6AA^S>8xgfUZle_9r zl*`&inIU{JMU7XfD%3aHNR&{L1kG_|$Mhf2N+cf)vTW=0P<)r?kXov8?=NoGXNJ+$ zXjjUe!ge2muxq^OV=X;UmGDY|!Kqh(6-4wfdpEHR{8PEFUXA+H+qlazy$_AgwPJ(;apcbebC^V6f3oaSj3FkkJtf7b zs*h6=jTyDYs3J#b*q^FDdp5%CygiI=KwYm0()#G#E`@2izIrFqERV54hH39_uy__H zhHwuOS{E+2FE6m|*)rUI5^56(s79iD45d4_Pdh=9i6X=H8!gm!S(;NU5SEg?LcBGz zDR|WR3|cuvPWkBe7J)VK&Ic`E@AwH%Obt?DxUiaY$9DqLVJb)5-Y)y|0afYF*{z&q zk~t@2LO$@q%_PcK+MvDJI26?Fwa^*TBW%5%w64cUtl|g9S_vKB+_Q?R=(0iJF1A<0 z0$tM8f7z+BympFTG?RRO|EvZ9VYjaiqy{t-9ovdsmih=t7%;a_PkDcO>MY{ki+0D+ zvy$+GT69?mffMrlV;gOgEoVpAs>KZFEon}f`Bd%>lr&g6#awV)~Tgjh> zy+QrdmZEE)sOnu(HC35%NT}dR@ike-I+f1}QStiohu`j5M!s}Xg*K#AX=j*ehfhkk zmmkI6`5UfFks})-iNd2EYRA)3EV;?XQu?Ndpb*E|Lh(&KTaDp4{OU*H<+nOpmjRsT zr6@~+$=^Pz4!VfAx_aj4wax(ML@ZTgrsCQF_q)!O5$&@y3n#=qdn#3M&{}AL0mJ;8 z!tAv)wYOcNIdK=HEZzyD7ucbxZ8h$Y`E7iqBv_@w@M=)T(#<5&hF`YS@UT6#ogcJN zoGM-|Nvxtd1^SpuTsq9TIKW27LsTmj|9<+S6pLJp_1$(s@(211+%4%LwQ{?#TW*XT z9~nCjV%!TCurQNBv2a3hQV|=%?jAkYGd{g&Wm~1KVT!Fb+6$fDd7HwpLP0xn_{1x* ziyAvs)=#HD9y=poW1cu68fGy40vYvsAL!y6QnfAjRL`^Y*u_3Br6ybk2E_Ps?9W(O zAPsp|z#W>1&{B+7!OdjdQ6rHnAl%tnr7V7pwnP@4=88^`ztmK=Rpj#a~B?Ese z1z=ga$fGl8A8;lRh-fkqUUMzxH^0~6tnT*6*TiPzXgi2IZCD@pxP$?dmWNF{^Gr4=u^QT$3mt zzH?q*{GcBj=z{7x@YY>B!~ImB&a$(h%9i-r~HtMl9XAN<}FJTOizMVdwx|# z!Z`x%dStnP2371#HaYrOX1wTx#?3OCD=%#$cVx1KNaNZN;QaZnl24c`<28lu=sBT!M>99xS zbGVx$?RUa!uYBD*j-mFe;GM09?0l>U$A0EaYp$vHI-Px9A=;=t42`WtonbY@*fH$G z0RCA%Xd_eKzyU%ifc{2c$c=?S`aq7Is)aL>4M~|P;Op6*oIN3WVqZo&;M3KrT#-(e z-;#V2F#c3%U}Q3L)JfDx7}9DmaguzN1e&odQedS&XH4zCw7_fDHD*a?Zs`G4@pO_H z&giF}Oh|lg1W^;tTNmE5Ar8v9ciyvWp`k@{v}NScF(}ZF?|_4FH^bO%=$V9T&gOrC zCe%Rr=vgedKST6d3O$BMsHD7jnC}-`?e`|8UZs*3tRtGW2Rm-d2i+-xjFE|&d;p~n z+BX7Wv5$GVJGN8hnY@eI$dSWbIzKLTxi2NX?MP3iq7M)Q^2CAgKw!$Z4(klNfnKka zlYi|lzI2!W{0;0&kla_5jKbs{)W}DBY2h!HN10C(uVICOLv@D1Mq9Q{BWaO=2=^|7 zo5qU$Nfj;b!`qQGlaV2J%qCB$)jP>}| zUdB|>Y|ZPKu5zi3FOZ}k6nxc+SD6X)Sb9eHUT-v z4r;AzhE7LviSja5kFXmEJP}z8@cx3Zh**(PVWjKooBi}lbO`;v6Cxxv__i~w`l+Gr zu1{GFal)!|N@Xjk_O!F{qB_UAD5F~cfgTmmfF@$ceKnUYjpt%`zi=G!BBUUZ`1&VN zLaGSD^lIwN4;D>_!V(_AMj9mm9t4Jw9EmeVO{lpw4l&@MDXow51x#}jZc;$jkllVL zO|Ssuo~Rg_(#;#)R8^fB|4P&T$(|J}ip`w;9({FY2bJ~MFBmuPZ~KT5?4mn^N~2M= z6bx**3DtOT_lWsHXQ!#>#vNG;3KEd(afN^#z?+XMei0if##-Q=VRuco<>?}$GWY|; z71sDKomKx(N+>{%g6|Vzl97W}J0Od0ey|;E{M*87D3B3=xX(b)0J2~219emM)zdk< z95$vRV0aoo@+~@6(bDFuOg!wiKg2TO)jN1@<$|-$aNZfcVfqm(rU1n!0Ixp~Zg)nO zeQ_wa9$3ls4&v)pZtksG$gV?;DzagN}~RPSuK&0?4- z@jZaBiIX`r*u$p>B<=F5T|M8SWt!ReQOlrLkKMcBkz-j(wmmjm3T*srv7gE=Dx0c# zZ=aB-milU&4wnRjw95!1`J@1Xk7XU?mt7>)$YgLpUF$Ke#APY&xdb)7vNSt$2IEj(ukW<5ak0wRZ75&P zyA9Wd75WQk#{smp-<2-jg2LR=Wl5ds@%y*c<9K;q-GLcnppY0o^TkvV>=J7g8Y4t> z`cux0TD;TCQ_4!Pv z`DB_@fi`M_-aT2$_?V`P*BPx+l?K}ae0Y$lNd(o5Y?{TYPF`RJRY09*dh>NU`1|{{ zTfq7{Tmv4tU#X9m_!|+Qg(qnbnRb2nYb{>)z$6LRF<*+U0cIxT?Zz>}J{#hMUes(% z??T9|$?Ag1lbSF-c?5#B%}?iFLcb@_e{5OyWItoHerQg$EIB!pCBqv<@DQAeIlX`a zGQ(aRN||Uq4laneSqCuxr`mf_V{5<@7*LpS| zOYO)Q!m!>2);GqR}f~57|EtN55y#m5;GOdt?Z3Q6 zEniuYm=3cAJ3SsTkmD5?@tF`9n(u z7a+NkUu3W833}+%B3=Mm(jA0CT~=#gddCWoiufS-c~vLY*^Z;6Caxrb_7Kzf2f36+ z(+#l=l+Jx5nn}r*SMIx2akN!SlPJ}js;UHhHWJVm61k_nUqS-QmD8>=9zUmkX5~)4 zc{?MCGfFOdx-8~sLB#|}Lpw)Vb`CH>%D8)k<&JG%x^KOtuw`|P8^$cFL7J^hwPh|H zy8RMx`*qoH8ZnZK6w_-6dC~|KUF+*ITAgf{ndVsA@H0roN6Ai^7gHZr7Tbn$y~#H` zsY^mY?>nVdnV7x~23!qDd7#9n+|j|4b^cu#Xtv8=)}NpTRl4&T;*m?wsA@c!mCZ zmdbJ{&9Q^ruZ`}G+LZ*OWa(J$WEiKRXi3~2)W}B1!2BuYAQctH#c{#O1l67l>h!t0 z<-h~cl*jQ!8kgv0G8v(|`$u#GNjdgN8NFl6C-Zk)b7MnEKkFZ5&Kz(;PR5hld)koc zX=4iKqISVZ507@c(RsSC7rzM2Jz}?YSFF_WS@EmXRvnko_L8nf(i>QjfpI{vzA;T5 z$qWShUm|d2GQN|XK_h2Mo~v04Xbipp)6dmRNCX7$rQ@uN)SgdNCeH|R-7;y z%*o(5KOknF8q7AWesIt>{Oaox$b4${D|794%}oTEAeT&`-zr8!?M5uHPS+?wDbOL0^ELMnWmJqbn1drgRMt3S0H6 zymSk_UM__Cp`K5?&nsb^7C~`zjf))YMt$+J>+xbE+mX`CQ>qKlsm|d(V&3BXin(XA8OYu(OTBLG7X6Yj!E~ zx{K}vwtTBmp2n%#V!FiXhve!lOByZ(PaF8OnsPo7Z=yJHsae9(k0W)agzc-$I|}2p zWk?OB+=5uIcLoCevqCtQy$c0qCS%(#UyAh>7@r{4kKHCLwzHD(4jwFaCr1#xhU={j zN(9tZ1&cOXESB&Dt%DzJIrOloA|s5uromW0Pf$XTx~IDs;oSog@hN^Og1(xsjr zA@rDKS(!bBA=+r)yNSa&g3@{xNaJgFSIB3i6nH9F-l%%o69iim)te|IXp)4^3BtOe zB?(P<=*}bSU-t?wyQ!gM`6PGwj2s9~^S-@%79Yc4c)bfeG35t0%e~EMI;qRnSX6wE z?6IHstQn3)G!??k;JG~uMH5GFALl-{O;!i?{ zjuwBre5oI@$y5Ia1hRoI~BV2QNdrpM6_3im1u9n@3*|E0+C@d6=(pV%t9# zjtM5XkZg3^pa{_F()XLsyt!H-q2U*Q&%*lQv&9}nfACQQKMRVK36yYP{tAtUIcqyvW zr<{)`bkO3Gwab!23A6+rQb~KHSfdwY_Zyon#<#SbM}V%WtN$nGT*$oNM!A~+D!2}! zar?Nx@lNaHQHfRHD)&H7iUhI^({89k*U(hVqH{suVk)b;Q$|+xuufJ<9mqq+Qv6N> zHaEzNx8LmuKu@2$hp&P|LerSp8LD2t9(1=@q^Re*e53I$hQ|!<6=Zb5mVC4iP-3L@do)Ydj@mo$pz<&!x^fjJt2C`n`~>QeGyXMoBR zZSlZxMS7O!=V2N4!sD!(#$M~w4QVu;V_7-_h$uWCYWfTGEHIpV&ab^lUFV%M6M+Kl zP@y8Zm5#m-`T0ht^4f7q!FKCOl2(!o!#AEg{^tP&(qJ7qlCSpW=`kM-nKz^*2n@q9 zA*z!Xp7B|$BhDm6Eg4J;Pu|yXm@I=FnLdhbzklFASinmQN8VqUi!EQZWhqa7#{L9n zF$k@=bP_Esh-rNyFq3Gx>bTPb=vu#S;ddsE3nSK?|&t?Sr3SnsBCl zf)dWF5btcmQY^Ao6HtF+T0fY!i^H{mESdKyj;O+?_7AC~mMrD?;_Qqc+*yLJYD@4g zfR28M5PZo0xnnjll_6@w5+19h2F*-ebX4gU4im0vc>N zx3>g&;!<{dC3FuH!{D%r${daA#oe;+K$u{4&{@yWfEV1u_4TJ=LN3=WekO-N(0orr zrd!1=_)sV4YL!!_#El@0CDFg96nmAz$%6g9=%NVA=%jNuSM7d%hDl!_XOKi-%HzkE zaN(b;BvR@yQ~(mxD?_H@zSH^Mxcn3T^Sq4mhW-K(-D%74oXpnwRxJvR{k zuj@LxmV8?}t1QBFgboI$SoA*{|iDhO@(t`@qj2@RUYO`h_n#!+5# z`UpjJ7+|14u;67-x`a=VX;-$zb<99w10-)Ixa@huD`fssf#KeSb76oRD}_TA31M+B z59YjVn@P0qS>^qzvZQS0(1|uGN?}NAITa~m3LmuCI8uANq|XVb#fBz*$B)NnigN*w2B{;wBmf;{_(pd zDU_euDcokF6=d5P7FD@_SnpV`$v?{OK9Dd&T$$cd0C@;K%|;EV+Hu>e3>miW8PRxpW`+q9{SD6~wzHsAhc3sKQ(G%@1m+VjT3~S9(=;SK?*xx{ue}JoHl=-nk zmMc=W8^LU+Wj`%5taC1o`%ZCPKWnTVFE59HGvy<7GA*>tazS0?-E=su3hc)KMu~Q7 zfIo|x*9<2%TL^lJN4?2F4gJzM%cCtaKG*92b3c8Bq$J`l6V)eC)`34irW=sen({qG5i8OZDvu%yJ@cZ z)5cyT*y?2oAU?haO}nZCpJ!6uhXF|ajn9h@A6XxwIZv_vhQ@jj%(IaWI0L@LS5A{SUoJGTeU(l7ZeC~bj!n`Um2vw$N7pcDkZ9R<` zo~94m87RRwRj_sTD;a2-62O#L#rDHbn>`IA=S%ZOUm^Opj^jH zoTFE(qxKHW#o~i(Nd3YGytqMxSABC}ye6tcah_%sLg%d<)r}0A4{Kz@6jCsYUKo+? zEW~$X6&X&zqu7;I6IQ`v&9t4)cx=W8@aPvB0kMikC^!v>f^YyIpkmR9?D9%CkHvE3 zEj6E&7DGF1q&Z zULSdwsAOrsekUGGQ@)-T=i{9&Msh~wGD}DF@F=K-j$*g9pfamGtf*R4avJ!GY39&Fu&AxB;Jqbz~E?>_AG?EW|wTi;U&%H_P6@i_Lh%? zb!aIfxi2P!f6~zw{JdlTzF?c5v#^nDb-yB3rxi!Zo4`eSpbaDX)UL9K-{$qE#|k?D zwH~5#(p9z5fVkrHNnLb-$EJXY6Q>+l3_{QzAx-xK~)ecaL?dC6JKh+oUP0$ zIf;9&y1t6^V|@{B@C1?>YSU!_&pHVmDoE?a5$u)Va=RHwse8+)C2Y`}m>W1&=!0U` zITbD*)UIraW!`_og@+%z)A|Wz^utdnkE=0|`F+8*9}Jw<^q%b&rSmJ6L&UR6DoHIE z=8dOb1?IsyU zr;yczJDEwRxie$){`Hy1i@wcT6f%{OGS={uZbI}=Q}jw%y=w$gs_sSK@XMq4DCk5X zb5ju*M?%$iM3u^~|t(1$!jzGn9Qz^Rg-`u zPkKKpw9M-qm?yDhd6+3^X}Jj~S+679b1R=Alr?|BH!9FN+tt*N`yhEo8}XbnC(9iz z;pm*|xu>wLNQIpHJS$5lzbv&()Jf{>jfSM-b2t0$$4Q2Xc3;+P!EBzEE*F+*lyNod zTFht5U&3Uw!Xgw@e#;F zCHrUmOr6S1C-ufNtTa_UFB3)v0_i@?rCyX!z%j*|pXc*5SPP;RD06LM7#nhhflBdA zVaL&ou-UBp7jNB*5Z~l*Pf}kBpBna0JTnYOIO%+-MQz0B;!|rm$XT*SH|Be*8H@y| zgr#(3bqW_xQ)8&5t?4|zTxyb5nxYh<`wWH8y4$7@A$dcQ%}jZUgdd%#v4;GY!@$ky!p=LCv!C!y2rzxbl4Bx-al=pirCO^Abb&|E+Dui&bN^!wUZ4$&Y zJGcJBHn6(3pE;FIN=Xe{5Sv!ek~e62?J5P(KikGj`1G_sr>OycFJ=_pH5U~pJDF~SLtcpHiF z&ScDs=5g8>)<-Ra-d%4?v^&=C55kLgkQuiwwga7gQi=Xq*y19~o5c<){M;p3~IN zV#QUjyo$uMb=5!$+ljf;x&^yhP|kMnzp2WoU3p8!cPJn@Sjw+$w;Zi1&3QcL*-yzE zCv<&LrzuCMoId0X7Dr3v1cD@-y%kpY{l@N{LEWq8pKt8mYdO8V8l0f`L4Ufm>F6m1 zC_FhjkpW7vCQ4QOr{y18RM9=etqWii3!?hp{kqZmJS#1R+=crGrPA?Mlr9=P<0cn> zJXcJ#ZjD5a%|$|&unK6vy)TuaF@>m4{cNhfXis@$DJ{}Lj>e*vss-rjtjiF=O+BGF zXzFdV5ObT{#-V&hXAzlWjaPA;d*wPxN_I;2MuWszTPR5^#1}irM#G|4it6%h|y!Y?44E@mg^r# zTSm1flB?nAr5~+-Xm1Tn<6qyxm;mH(f(%A|43rmbAJ*&b>)&$9ifY)F0&-7aW_2YD z#k|6MZXpPxgy2m3FH&^%g$BC1G6g3HP|Q2hCtV*vEqi}~=pbS+n>$??__;qFsZ$N3 z>V1B-8tv?iY-!_QIDgMOhq#4rvRv|HHRbVpDKyfIvOpq|?P)?}(70F~p)*(H9rs{x4s7Rrx@ zojx#bM03*w;fhQ}nbMW3jDcOmBZ1M1c(*8v=37yCw-L-U^6l1KW$5$oanb<#b2)E^ z`TdPH+N6+nwFRNzna9DQ#?y^2;NTD-RVeYt`H4Rszj(B-782q}QHq+qXk@_zLzbbO zY`D11<|TQ?D+rPumMgAoIf`MXiV|Pq902b2rq;gNt!7_BRb;3}6;-aM!htL;i!Uq} z%Kg1!ryqw-^W&!}cRNH$r@Fq5jL~r2x5UvPfH(YBhI@A*Ewa8Nkm&UQu^y=MvE){) zDu2v$fD*saDmHWyt#_HWM@&m#aA{Z`NH@t{&kc5=-P)}--ludyfqe@DWz9Dv~OTAw7 z!{*uaNj;6#hqN{E=+h~7q4Smm^m)Y8E~|a%6dyXILpQBH*LjE*a^O=|z3ei=lyw=M zR;NoEHl!IyFbu445)sEZxBhIkdFo?x90hSl@-sVD^P%GJYgNFd>R-hQrWJTyQ-0q;K{~qJn5nlyACnxXkQ=+s759{{o>_%{14Ft!m?&5gx|5u}g+~ zR&pX;`o>YYOZIC>MA3{*eWWen28G0y^#2d`-a4wSwa*t1g%)>rEu~0tcPL(PAL^YouuEhh`L9fZolW*iO@F@RC%A{G9^qp1Y-rz8ywuXz?+12Uy* zeO_m%D9OFJ?0x5ljnw6ihAU<^u zy8eyr)a-PLKU4fW&%Opl@}E_5!0Yt?p6gW6H?}~1r}ioQ7ifP^`j(W|_xt#trY`*p z@5%Hlv0s@}mp{{QJX1h@Skm^f?lFED$k*3*Vuhm-=F}e>D%RTeS*Xb5WW6PY+%-Cq zpmXhfjHI1ZLKey$8~bLhjK5p=nyVu9{?L#!iq$geku+ZsGxJJo#fEU#%_<4R6jm4+ zms~sk#6(}0qbBqPP0$$Zq9T`QbZ{hLZ-pefE+C0QVjTUuK^|^C(j{aGmD74-LdOYN zEl$kYKecaE7}FvSTQz{c$}e8p{Y1z34L zL-vOBMy6;n^yq>;a8PU~0+^*^LKqMMu+?5EK5tvA8R>QQ>2mJuX=9UA{n&VF!RPKu?xJS4IbB+xiQPrxmhvZg`uC}6oIF~*x z9S@H(OH6LOi68f8*lPa@jbDff{*Thqt&HeXm2IQ1A5g6~*6Yrl%6xF)nrk7=P$zVo zi^@IXXnWPxkgFqk?$7L9(J858Ffvg=d&Q|X#(k>M&r-Fv7i`#B5-d4E_!l7>&NB2$ zw)OR&UL^~=WN|%lm{cL~rw}jK@R3UD27l9r?R2Y&kCE0kBwT<;2T%h9yA}DEW#)%y z-hnD)V;;unS>mYnV;HwqqZ=SXH=O+2MD6{Epz!qNI9&RA@_fZ?>CirV7c@mp)iD zQ0~w;1Lo@p>pLk2rGu4+m4@OkmxMT*XM;tclF{CRrDaq~B!$CCS!kKc=qvtGZ>J^R zz!PS!H)gQaLe%G?BA-Qh^bd_dvf%YStAsUNhKfJlvn07S9&>FZJw?eHu;@XitU02l z>+ZeU;Kt!|F%+k6f{$_x2>5-dl!s!+5`mLO%S)q}4CBg~9d15WIL83cYNsG>q--hJ zqz`JMSlb+Lcz93mg7}m`q@$nI>E+Z}Byw?8=EavS#8ZolgP64j(4r{~y@#Jkq866!%oTA>-c{3H}qP21K5mTb0wui>xPwI*NIr*@% z)>1TOH+MGCwy>d=iAzk`mn;cnRB{TR+= z_0x)bW{j`i#(=&baU3vCTeLCW7IEjn-Rlb=Y*Qcg>_yQ<9 zzd(c%gd3(oE96rU=e1==%Jawfzb@t3S=r{L2XID|>fLvoqtR^z{{n4jJNfGP9i&E(oBtu%(MgXpViSQ)vv2`PjG;NK* zE=r9LO@4%ur$fK|DE<9L>zj3@zUD*k^#>&zek6g{$+4u%-8h`D^P9F5$3aNtidR63 zEOuQn*p&y@`u(EAu;)G%Gy?Hy{r0OGEVM6C@7a{TpY@(}iv*GT3Gc29tx^Dl8n`|S zSU!e*QVi1*@4I$cQ!(YDaa>Rv_?4oOMVqreH&WpKo{Kw26s%AtcL#`DrBgogu#CV-VZFQkgJ7H9=w29-vV*kBOYk#S= z2Eu+D*fIT2=r*AJ4j*cdxxRFZzC^yK72;WQXC0Z~=mE^pvzFEIxv3H3SPa z=3nGLEZ2I!NuN0mIa@TvLW1AG&N%GtIacdGQOp%?nYG}{%Ft^8*a7JeNJh8YkB;lD z)8shg3VfdNxo_}>zN1Q5*B~Fl$-7rXYSYy5PKf<5=-#TaEARC`y04qlA$WBT+t8%k z%Oa_5Rov+kl4AUPK*8m~@W^g}clx4210lJNAK?sfYJk(MP}RZi0=5U<#LT$Ot>;*& z58!D~7t;Pj)#=Krcd_|9dTfpc_+8S@r}-Im{}qqGvk}-OGM#@a0rB+O0&ec zo3?kr(#i4Ui)t@#9BGWortWt@MXDmi;rw#w-K#Py7d?^cq)_c z6{|QDrwe@$`QY0dD^IFU(r#n~OE@No81N?Xmzsjr_eo=c2XBCh|D1y$cWN>vt30z} ztFmlROv}a>_*kD42u!R~KiQ&NY-phF!PM>KBslzT;Dm3-E`*_qIlCeTDZeHiDYnyV zPrXdsIb%X~BiSG?`SEAWU`)?W)&>pL4@4?po{Ct#2$*n`uzW+hIA`b4(=~Dk0J~F!~0l8tSh?=jA zgj?}_!H=WW9joroP-JL4!dnyy!5Upo#CE4lr1zO|*7@6y&eu*&Ic@#OrFa*2(KqO7 zX)4xXR7&+XOu4-@ETLSiOQ3h2hsMW;hB(zo@sPne^U1yOPc*b8kC>YL2s60aK>J~( zQoT~2-)CEH-El1e!eBaDh~o7_`t=$=k~BWJ8fnXCw8%KeO7urtP&Y6bQ3ijb9yrx| zeWHAgb{bX~dN3GGqIN0~%eq4D=y^EP0`1XS^6QGI|JDFiJ2~LtPg=+qLbfOQ0KRGg>be|zu7oIEi~z4d3CC5bH6S&t5C=2vKK#s ztQ{Z8ZC;{>yre3f=*4)YEt`S;xO3D?HyJ`CMLT_mwfl-rAed^vu!l&LcvuEC(O>j9 zq5?iR`539SzARZbaTa#h2iy75z)6lh7o3fACB=n&3R|hlU4Y2drR}TE(k3mN=@FsD zQb)F}pbYdg0WnLdw^g%CsE#a?Vrpl)uq}|%jBM~B2Th*8a_4~7cfrUTl?4NOkk|;L z4i2#=QNSrp()Ww<%dWBszKIR$;CuSWJ0VzBbZHAY z{NZqdoolbd{fTggjRciTUsG^|bm&1jW5d*@HEy+XWz1moV0zFf$L`ItaA%#=!|LR{1?I2p%1r6~ zsV}{Fa2F+kk%)vTT&cgFj=K$}+r9YsP^#0e-rC(?ljd^+Z}|)4e81N$LLP3f4Y#iH zSdXPNRC(|%zcLmQQ5j-UIxGhgM=^-0fRPgT)%{$;qB%D37l@P<5hPTx-fHAYwG)C&YE)V%r-HgZu>_G`Df!lx!HHGj z?7P~A;0Gx7ETx8{fwIKkDn;c7pdy0M_HT)@x!}(|3r>+`xi#=8E<3@;x|az{qPo0S z2~kH2A5Z}P(S;~3Iibt)m}%J)3yBW-M;3T?qR~ABsa#Vdr_`d4C~!3t!bv!pF&9xu zKzl$<^oPPM6Mq(qoP;9ny_JpjOL^A(6W;g+AaLU4hw8QTb@<6fu2OR_^7{&3 z*_6$8Cc$mLc>jB4=kCQY~ zKNrpVcmPYA$9R*asTlkNfE5itaYN8O`_lf@Y+soAJq(_PH9Eke?)pF(*g7e)&G`zWEQ{_X})$f+cl-*v$syWCU;<^L8 z8k7^KUhV*c=@lN$@KwAci~8Bcp$Ns4=01zqZB&au{y2_EAhUy5A!_|>3n1F9H9mxx zME#DjsFiy>aY+*v%z zhrG`kH*O2rnMn6?!;Qi&L(Om#vczuQ5_uBdCZdDE{$U1Wspz4}56S$RiLhZa&`tkL zsp_fNI!(@bHhzF(8>#PjQ(dUj8m*;vr31D`JlX6}j6$4Ymqf#lRYx_4{e6>lsA=%^ zMLf*T6a?XE3>>_`u+lStRC2Qf+}zXDS!j#&NKKM)$phiH%5rirCA4z}9o4%@)u3F> z1LgPHFVG?PnmX&#$#Y50z1alM6E)5!`=@Mp4|JGC)^ul0FveRfz1Qa7kA=G^xL)KX z2pPKZ)(`hf${dbOJu4!z84A9o6~ISR0_0LZ!cto~AV z2xgd~ba2I2JH6OLgGj}-C$33obQpBCLr6Kqqm5RMe)J8^F42u|GjHn1s&6aa$5YG$ zj@|e8)6HgQQ*Jl4g}LZF&qqLm*t9!nH28AfVV4aSf z_zLf&ne1g^DNC74&y=~c#o|!4GEcC3`bN(8ZACtakBOXr(?Kfp$7y>Xe(jEG}R39F&)m-O|bxl2VJ0*ul5-62g93LSUA5#Px0fF)RoPs#u9CDRa^+{%JlX+^N z-*{Nb3fRc)pt8KF1;^yB`7M~-$Z4W3*`-V8z_Txi~Z?i-hfT zEl2^-+&PO+H+1W0`cE_mRV+{GT}`6z*Sxx!An|lm;aGgss) z;~+n3Um8GyIIA1?je70)`Qx4R{Ee1sYs>l&j{boMyeqBtYqOmxzEn24Ri?zQ#rwru zMyiwv$G|NB`N&HUf@OG>ghhqD7EN-sJ zA;UE9#gC2nqQb=W4_z>@{YA=R??bTRN8g~Y0jmb7>xHAE`Hf_>x>X8&MiI1P(EEFB z10OYh7Lyc6X5)!(BqiOMQ@^FSk~sh}xY1ha&`j#-^M)237MCYw4-VT!LB}_%T%Mo$ z+5_uX1gANX`srjyw>HN%yfKYM&o8Xs7S`!@S@ z!ac~~Ad$c8S)mi}x&reH%ECE@5c?IGn#A8FZk;V7MbGcG zj?^2gK81k%lJw$QKDY+c77xDSJ$bTp{Tzf??iEVQ4(=3{s@Vhm79Kfv{32FyAv&dY zb-s^aq=SoguYAp=ZJtbglS0Sh6SHm$%g1NRq<{jLEs=>ke4v2ur{k)F`}I;6J@gS1 zj1jEI$B)8w4fy9brzBR#1o}&1> z^duEJ*J4~^w=i;8^7ArNrY@W$++waFuCY&4l4JVesWFKXV{{0glAq~l<`8ip9goMm zl?{IhbYR}obtT|OV{gI*M=N&oboFqV@urBYt4{ZqrFGpk*6fW1%|m1-?pOIexzWTI zEHElMjaYX<*NPLV&Ko$b#(?MhF~n&oh5*U5P%d#*lyrsgI9aRM)M{37WNWL!C6B~xAu^_ zHCwiQL3Zmq!Zey7#>a)kt57Z1~3?UEvB z2*f8aTwLg;9?j2Pq<9G_=O3qWHi3mPD-lAM z+Qwb%enDY@u@R-|7zeTPI*ZFvLL;sHZib%LHN(^-UGwI#l z$%)&v0f#d5G)P~BAzZDOex6F#|CnoS;=o{eo>kWtyFbqJxvw^FubTh?2P7waNgZ#IS**ytyobML_ zZfIBEQ3-i0$E|DJ%FtgWuD@Tv{T$qqumYYwz5Fb4S)noaN-~}{RDy7KS6ga^BN+5} zfCObb3kyQjh5{>fe{FqqY1MQ1{dS)LcQ$^YLQy$`i6G`foF7$2N;RpZH@)nxYRmut zCCbLgzoMh~6ju_YQINgZQa`M>>8(@ro7fnt9R0by_KSDF>)-vMHKa7{FOcmo(AASn zwqGDG0NJL(STVaSeJ8=yzMtUtlc}Ad{f`d&bNw^ssE+B8@c2}5#U(Y+Ll^p&fg~EQ z=PIKFT`i6VtCE#=!Gn|jHuhI|zEB5&ulI1cK_@UUv~r9+g8a{%mvir7&Nk zm{7?**P2bSBb^2RDo<-7|D40!2$1mu)(ue9SkkBYO=zf1OrzfxGi*vy6sR4cFqw|pVQ_-VV963E~0WG81hgn`Ek^e*nz z3=8aAz&XUzU8lWXL`dW4IlVnDQ>hHa3AGCtI0v#gDpaD<@S(wX%P{s{)UBi?pIH#ft^$bkbf@n) zXqKLs%dBaNTcNJdjKc8le8OeZaf!U*URzKRvZ#}feM5et88B#8yRJ(< z=KQQ!2~NSV1m`gk*I3n^o&Z37XjWj8dn>H*j#}S{!r>KpxclJHqtVV7O3yuxl^`KL z&9NyD$O|cLNU7p$`u-(g(mP{{3oI)Uq*xm$Qg`Z;Lfs;cI5ck90ZL86fB4;@^(O67 z6BEMX7wU)QC_%Vp`&8KEW&ibsErr0aDNB6%>)H0o_!KE54^(RUXIDS-FyKuI;#vZ> zLCwxPBKY>FweK|}+84_e1%z=jZ{I!eikflis0bnVMVmTWH2`n@*s-nNPk-w^c)Ol< zIg4}HP|uWp4-;omHZF<`kTd|DXhycHyUdAYE+!{R!+a5XvB zNnaBhB#?!8Dj{C9du+Wkqzg8A_O#m?n{?FK)Nu$cA@A#JQKtsK#8`;M*4(wmUfZJQt?HIHhCL%&iIfh_Fa7%;6d_#^>V9y%Q_a@OcsawcYRtFD zasax1UXjBXfM)23p22tny{}U(7S_D?sh&TRKW=C)pN!#+HyahFH z&zBL+#H6EqPuCWA)CFI47b&~$K;KJNX=q=!?{Q)Kbm9}`0yKwSb*8#HW^jR8xoWF} zCzl3AhB!}KiKE%2G!MV8&9M=odgCUBr zu^w)MpEJ^P!pX3`%AOJq5hu>JgE|s?)x-_cu4VfT;h;kYtCkpCgAwG9IDQuIcnqW9 zoeG0XWu=dci`$MnGifAOQLZOEohH*LF!jrBVAQNP{srM(YMPK0dI^4lyTx330qnh3Nx4rv$wLKH0pK*Sb#ZWMN7uC#tuT(joparC4?36wK3? zfSVs^=513Pruf(hX0C=IhKW$Mn`+m`? zwx|3~V{&Urzz&0JOvh9cXmC|fEPQnhcou?jnCDSPXE30~7jpmqv3pk16_!{`(m|#ug0TQxvHr56Yf(RQa&i!ZLC)$mL`IgvZKq!F zjA5m?mfH5bAZ4P1ANNQ)Y#hA{6IOT;FB%O+SMDXK^(#)FN67X^K{!eyuTpVdQTOsQ=J*$Fb|AKc+83HLv6WU`ko- z8L>Ny3xGx7ejx{d-+Wcmm-p}IobH( zxj<5|Dqq=GfqN6(pj6=#-ZAWOrpmS=7h?q#?ATwZjJm5#B|jMqnB zwon23g|K5f2ybMM31QuMO(UVBz>ma(3T7f~WyNugXHn_)|l>&;_$ z3(9c|Bd7`>*8z8$WB_3Oii*ooZ zhM%}f{)1&kYZ2;D84|QY33IIaf>1T(c-|DaGiW=Xj-I5k>em`^W!fQ)Ep3bcpu233 zaPL~p&MQ$7mdfP8P%gWk2nLXCzItv<^cxY z`U}jo-`Hy?p0(FXV9aO=K7VlJV;drP>XE%rmN2xZ=>^gUwA6-$D=4gJP>+1rh3u;y zWo#>K;X8fK@_Hv%Xh&O>S-iLs%PtZ3;w|8O;7{am$&OViga2yn^^3}y@6?p@wC$m0 z1s}EFDL#L1(s4cY%yL*s^OKYLivL5 zy>hy%PJ$B1NlXpV;Q4rIe=WZ~5~h;-yvR=rtpqxkkC^!Dw=-AE#S>pznbz_xcWrP4 zqbR!u9QPVvJSQfcJG@MBllsWXbDqF+xReHZYwJQAb<(|A*>$*fbE+Ak|IigJOcm9? z!=3JICYa(r^OjCWad*=i;{tHDC{$0GjK6k<7PtUzI;7H1^ zpC;n4k^XszQg%!Aq8dm0d`}F)y@5s?p4R=n81a+G{$k2F4t2apR3IjDltP4Wzeq7H z_GW`CUQFdqI$$ZTGlBEmKfne%eRpr|e@e=))x~^I>%K3m=x!VwKGLt=O-A;5BBIRQA$fR(A{C|xMwnY0o#aaW z*TF}#g2areNhwj!UM`~ecPxoid|}pu8h=JnxXB%VVIiSxjZQ>Z@8JXF&j#MC4K+T)QY3aawh*?f-Pzw!{l4ZY!x+=UZ&l3!HG3VxO4`Rsk9DKN0Ei18gtS`SxRGilzC zbm`xa4sbNDgp=T$iW?{Mrh;B-#-VnAw%ZB_lcFsY7rwdxDkVHgOUaO&JGyCJ-Yk z4+|8+BKsW`DYd2_Rw-M&1V$chDCQK7SQRekk8)Ys!fZRe(cXYLc4 zFPFUfn6EyS`Q5M=puzio99?`p`rd4p2~G9nTfNp6-e3#8dinlG`^E+(j-+=CY?aqvD&y-cIDTnKe-RBf5Ii{$PG83tbTp(ncL>t zMMXpUu?+qS#Z_^XwMFPK!{hICxs(19_Ie2ZFZT3UW(Zr3cpQR-zC2}lzg!27x3kTS6C3`?6s%!&L6dzaCUtGb^r zg-N$UC1TEx&6$WOh44QY<#Bvp?7E->9C4*Yg4R8Hov8Pz1XhW-4^}lNGa~cYC-^y2 zmc&3_c(wKyp+Ghy-M1hZ8sba8YN$WI_^?i5%SAaj)(u_MNO$%#YS+ZrO;NMg4k6!37Ux9{M(GS^Mn5Ni-bF_S~%nzzwshV zc2OMkeQwsLd@o8OWNEqln{BBtUv32C)*bH!%)YyW1J`1MKAaxAO#&RdQ3SV6EZ&7G z6LwU}vp5ca>vPc$nQXRSXhFA!r%i6C3vUSih`KgimVJX6_0=D!Y{)oP@vpy8AzM4o zq-thxS=4a)!Km9~aNr*M1%j3P@`8zVTb)3AuVM@K9;jd~ag45Szs*z@0ipD|WuZ&q z@6i#?oFCJJtKT=xy3N!l$YKkPk3Wz-A>ciUH&q15)*kO@1URb|3Ym@2E zOAtSR%DlsqkZo#Fk-aQ6N zA#bT)%s7%QEaM4_b_`JV@0t+$+^8e)F(1}0sblZpu|Th$qfe|eGgHUzsFE-&;MtG0 z(8J|BefjF~^H+NLV=#2bM9^k)%uwqcSM5M-`3ts5)&ed8h~y0Epyw?%Zb9kTQAzy6zvf ze*Z8Rbw)E1QR&A&fy3G9Jo25A#l8Bz}n8sG39IHPCL&A**n4n~|6U~l~;9zW$EeCUzZ?lWRVy(05C z=Lk?jgnd)rK6N^OI?A5y0aoBhhe-|S;|p6($3TU*#Ukh{`3y0wKT+5U`Koa$I|}|h z%J<)!7-SQtyYB;%kf7K8;^RGxCI#v>_xgR={3S=ye}InuXZ9Tb zH(viAOOXGHoyh-Kf;|7z82-PzyZ_h{#FKIjU6pnDYjQB9e1>*pt!im#vgWy^_7~HkcTRcb2@%LAXKa-!RsK`HIt?6gf2At zpsvM6OPE?@GE~=vb5c0ISRsyz^VK=xNCDcaPr~63Hn8h^_Bo6|-L{Xng`K*~d~RUf zP}3l#z_NtPm$lVtBNQ!s&jMnM8+HIATra)V{W}ymwZ5$L!G#l;<%>W zvLi?fmuy#YYZmPj)JjxUm}!kNKceh6XG#5djD=2X>ro(iS4*Ld=l%TO1S_{*qO=-W zl#GxBB%q1l@p+yVf8LZi=N|5*E`M?N#b3$|QF8MXVYpsi=#(=;J?8DQOJhre%RRy% z?(tRvC#E(Un5tWLwK2dcVD7Cr$=A_~4j-`J=!L)JoVOmR_$*<4$BlRd-`?LaY9nri zyM>fF-1m>UZ=+C!zfGBX(ZF!Ok4NmhBKz!^6iJR~EwN3ss~2+jWdx>h8z!^*4Z7wD z%`b`vuz{bkyKvdV<*f|QZx30o2|nBj6qSE#*|Bqt;$Z~KMjL3lN_GHq+ zKLxK(eZw&rSZ%(7X0%PT{B*{2!x7}h5)ZWhjUi*O}6u$5YW?z@Zj&Ni*0 zC&NP-*7vz&d7;mDRf6rF;$aZ`=4;emh%K-Pgg5As0#1SF>gYW~PL|>;HB~-Iuj!rT z0{ioQNZ}s+nfwTcJh00QAdNBRJrpUj=}d$Yu87&G1LU^adMhIyPUva(#>P_6v1cQy z;rUg3EM?49H+fr%_M5*z(SBqQ^>t4HX>_ob{&^O0OC{Hmxv72jeZxU^+5IRST@Gif z8wI3j;Lt0or4^Qlo52J_Feh_#F2AKqGS*Us7wE=DqH3VIgHQS0&=1f>aZkHl;=v%f z1#Ip>DIZMU(|jDB7TxyQa?NlDR$??%ZQsZ>xkae&3&-OPmY5CF2QO|obyb8>`G#(A z({@7D9Mr9ntpaDL9ZGXSL^-iKnbRnjpJiKzZ+?M1ChV~FUhAcsCJDqIAq~e@3j&nn z#8!fe1xjI&%GnrIMIiD8%~vBJpV$iUH+k5pyhq8f`Hg(3a>-Kr@<4fxkp!;LkM7uy z5y-LI3WzXN(%TR=7A7INp)lo8{;;yg^o_nVGr#bPv`NxsRzOV_(QX@MT^(GgR8TEX zrD%XM(gb%5yID9$VbvlIh{rQJ#2F&c)r`ll;>sVMYA2)f3XmZUnleO;Sc4VU07wCBjg0GKf79hh#C{A+G|V6E>B zH|nQPe|}M~rjs|@5driz_oF+RAEPV_Um6}wwe$fbAxx(9xE35NBoh8|r{C{B*zJzN z-jsMvA4J5OehncU8bae}h?yT#Jug#(TFoTEsuz3r8M5>nleiRTp7*qv9bwngd)EPi zTvN`BaZBYy%QrDJ0ekFfpHs}(9zJ`zMmyLprTJ{O%X#r`;uK*b9r5ap&AT9p$}+bg zgFL-WfBqYo){v5(%S$&}N*El}VT!}$mf_1fb#9;gw!M9k`audvW5&qm*ba1^hOuc` zCDdY>3)t&i0yL_(vUJG%9#%4)-9D4f*RwnBwHwlrD>$(4h~^TcT77eMm2P8e^$iJO zYIgl*|F+q|68Z{i#CZkT-NfW0fO_lzx9s}LdYbdigHTMFwA@&Ut0o5JEE~Vq6|aFeJf=xCPfhTCe#YiTznhUPvsO%+^e-R7_VY$|AxTkxp^c%n*@Fn z_}J8O*VL|Mf@`z@$HVZ!VK=(y*@nNM6gZ_ci!U>EJ0XGmb?ng1T>jsDU;dsw`)8l$ zc8B;$9ESIiRIEH|vr~HebCWg8$Hi|otwjR0np$-8}$4IU;k?g3H@H5{}&x!|GPhf{jhyW z+8|%|H}ivWw6j03M)Vc(9bY{$YWZy;X#t9;Mf(>T?f-%2pv<2xDW8A7wFgG$fAbjx zMf};j{4el6Omkpz+8%BCmEdnaga6QPK>s!UpSO+VUYJz}#iSnCx*xhVSe~4wMJTZi z@Wu_JTELOt!ND;Dv4@*qcbl8gX&khiaF4P%A|S zbJ^qbYz$u#FfGyv%2vbnV9};qdDF})cPBkiv|`plTr)`RTTI_PnscFoR=`+g1rCB9 zHn`Z)MijCI{RIjSUilt=6@W6~fkSnTJ16c4z}qG8RgKcv{%z;Rm>OwF>FB_iN{?xo zPO$osOvccIaki_~lK5ItM9vGqkQ9@Dr-^g%0&-;f)w_vntM;Do7Gmfj>(cHajRhsq zgQNvrhX#j{C`-wbuC~YafcUIm1eIbe>}&r49c$_~uE47yr%)8l>uJiH#_e{w7WFkh zEO4NDQ*FUTBGxcPaUbv84<~%27|bb{g?=v0Q_gQIS6qtssOK6WQm;rg#VkV6L(80S zqKh-0d^fJtS6-66x^+$KhCgX`hACY{P9t^7WmqO4>aOU=FP~L^=_Cqaz%kprtY~pO z^Lg|0sr_v@1z6C4ipBbS_mB;X@JLHnrzDD|wz#tM6wpaV*jt2ANvSAz14M+Bz^um`LBTfX8I3hq<2 zA1bW9cM$7V(=T_brO3?`$q#7Bp}t;&DVdw90pkoivJmX(SP=S<<)!ETdAX823_Ykt z(v#~htDr`E_~4LPoG;li548imk^}|b-j7dbPv$EIyGz}O);LufKX$T+VV%mjY_y{8 ziA=v=K?;Cu!+q&?e}O0=kMqH<&!=m#OG@lRShuA=Che)=Au6Uv{JN>6!;*4-l;O@q6u!7`3MaQC>OrtUW9Juu}SbU46UZf6+6 zaWz&t)NFks5Rxb!_Q}>n`6^NHoN&CR{|DKHH*50d{d;B_*ty+y9iB zd;ueguk)8;xGiwBnrx9?TfF!{ZIeG`T!|@)r6NLlAsqzK$wjoQewz^}bg2%Mv0BVj z-t;9cEcz+-DD3U4S?!>)Xpl@mrHod<)MVZ`L_7!VKJq?;j~VTG6x$O`olIae16}oB zG({~#q{*}cEfO}Cl4xSvcVGBYLGHsIl&txP)*6d(l(^gSlZ^N6M)ylUHaS1r&dIS| zxc#WCLtYXG3HeqIR9l2-(Cazis(I%`{sa`1dj8asDQ!E;Cf812WYrC#$UPi0Qw zDjcORi4l9H?{CGrejJo#pOk&)G?z%wM7w1}-PFXp`>~40=}Ta^AK)nQBT$j|HnsBI zrX}Twb9;?s>v3PjhXubdfuehQj@3z@@3VjiS?{G^2wpq}CX)Kxzd-d*INiVV;to}6 z)yF708%Mp0#F3*c2(LM(CIz9QemMp5>rorGkY;6DnVQ2C#_)7~wEQ`V1-7{2=!lXzMi3XMS`)9## zbAN%9>RAHkUdPfu7C~QVmu;h61_x=utfF_q1N-@(=JD}bzfDZ&t#+OK`lwqPecrgY zq&HuAHfrr2ZP+uK5RP=`v+_Ge45??6r3w9AOfaPi5|lS@2jqi|^PK@F;x9SkIy=GL z!(b7B;!*H#LF3;4-@u-eBry%XuA=x&6~Ex?DM#3rchGis%|SHy?Iqy%Bq0sm8VeCP zeh{>A%erV#BTI&G%qAyH+koMa|I{_|rqh6{{By1>?$*dgH;S5q4o)zJ zuKW+>-_S70JDPPrIA0BJ5YQboX)GKNJ2}C>C9|);t@hiDE4=+s)_#V`5q2B;_Z}S}pP|2Nj{ZYKRe$Vq zanD}yTh?*?^f>B&cLMsq^Yh1lGZ{~!k=~oh`@>a*-|x5Jcc)euA-z)b3pA;0e|`yM zwXxVv*PB__7!?EDe{eJ#`~5#ll4ozC&jj}DZ~LyPKqCP7=U*MC|MHK2vlq8ZGOoV~ z1%`9~tUd%*APQpsM&9qc3talvN9h;nOK!!y81n6HPWxsfIP0zr*f}P&W)Cv{VT}4m z#Vo0u(|H-<<#mqm^$e2j9{XRgw)_vlj=*{TAq)4%e_jD#pb;psyz{{?6Sg_|q4}hh zx>YO!q=2TjU9}t#MVDpJJ>9~ey*E3)M-?2!KZfo2>OGF@RB_n?8%j8p;((Tn@~mC< zPtdL=w6Yi2H~#KsMt!Km{w0K?W+&$Cbnre6&mH3ouoh8NhW(ZO7PeFNhACeT2FzuD4?xU+LNFnRg_&7 z1rUpW6WS>6YGa`k{e8u$r5Nb=q?Qa;306keL$vpT6bdV^ol?X4Gz;^kB0$j=&l=)9 z!f^&I3QuC)8 zY@sUmi^pmqrl+5ysbOV{gDqT}+wkp@{|k3~9s^O{nNGU)gba3LL^aX7e&)qF9an1Paimv$kXQCP@p4j{g3itOwQT*e#?okbj#UDga{EvQr)D|*s z|Kn(4gL?Ek!16!3K}Y@CR&@*n(|>_{+6w$B|86P%{j&TkK7a7xw^&gGFxmha>Ob7H ze8ukoUe1ZJgx1}8;a!5fX+@JC5^U%<%km2}uHY-{`v*rshW{)W6tpin+L~=pWE!f3agIoYMQc@RD*r{%p$r$<1d0mcd>BINHSMB{yUJC)o0~-Vv_DaXOG_FL8V%s7d1BCjg|9k@3^LoFxYBBW(5)rCJd6tPKR_ zNy^N@HiUx}^(?OU;!9p1Aj3_ui8>WwXV_^2*>aC-r+6ft4^o+l;4@6%O=CmB26cMZ ztEw2A_ow#Rs#a}b1&TQq=bqMW`$VA^3ZM`{_FFwkbPOH39gPY`ohLK?d^`NVYy1C; zZPn=cAYrf5!=XaoqR}fM@VgV@*e3gXRfH+C( zGI5m$2l=}1rJZm(f1r4Cv7Ji8fV&lLIhVC7gEx@Nlu;K~u%=W7V&>5aQZA)fbIaE? zRPK4|CjN*%W}ir(`wAF!5$i324@7-A}ll(f(zy40L$xyYLiiWq=bu{@<-h%wyA`w zz(T|PY!Bt|Ne>zWxv*XO-w}?BEtwI}5T+Bq67*K*P)3}u4M<)KFzwti5Nw>6e|hDDxax_&(D$aYR&Gf@CcQqt@p zpgX+6jW|UQ@g=cc5s!YQu6pjEwQ(~4g=nclTt>7*QnXxC6A=W5c3p>j^4K^O*YM%c zO|{U$Pvwl@PZn8m?mS{cJuu+d){O8YNB)28y=7EX@BjWggoKoIhf>nrsYr>GLBoKw zfCvZ-Fmw+?3n<+oF(N6Aq=FzVL(YJN)C@Voz~}7G_y6R?@Bdrp$vNwP);dpz1#9hL z?|t9*`?{{zrC;R{%p4WQJ>sE|Aa~{3p7{DgRG3!~;zHR8AqC~8qN;{^=?&0}_*+;i zuX}T51Uy#)+r|YE;QS`)Q-affKt>*;UAov#lxy_ia1LTGd&)+Ipk=vcj7|#Z3DxN~ z>)>95O{9Mk5kXM*(5X~-cAQ-4D`{MZNOy4PLrW)pguV45ulw$R5?D`h+{EAQWEYt3QbXYjMjh3~yhRQ0=UDQHc#29wh2KV^;FI^XbtQVVcdgWh*9HK_J?SStM zDY6BKjqG65^%)}Wt=`aZQ;?|7Qtj|Woc@DfobTY`!MP{b3L8g*lecm4hSOn^yYuq8 z&&19h?KAYsdH!~NwT;+jDW++;-=Odun;H*QaCHaTxMUpiqgHUS9Iv(g?@91)<7+%RiTl(FeeE_fdS>fm9x=n6y1O4Er<`-BYc zC#kzOL}GXi-WpEvb}v*Osn9;xkS0sfVahP)l`mQ;|*r&Ws_gJ z)!T=v@nzc{=O+)0d-aM3@z#SvC$(L61Cg(5suuUmGDhtSC@K%7m3>9f8i{MIhB?vd z;z+2#wAj$lELQu?v07J3Bwue@}2globvlli; zijKcbh@U^>=hBOlS9)s9oB2dqvEb=1?_0is!9R&-&Bwt2p14Z#37duFlQQ;Z7^k%)JE0F09?S)uKNdk7K08mut|j~b zZSIZEmd{CFgig!t`|75}dr-!k1T`G3AB}ivjdUE=uBS5hb7uEejHXLl>+I6{QpmG@ z0Ge*AiT>OcN&Acqhz_q)&u$Zyc&YXNlIr6dYpyRxU^QsSbxoHV#)Z|(0cM?ti}oT} z`JMU(gdZZ?$OT|pmTsx1Tq0}?$KLyNtfm1d*of}1zI2XO})&@S9<*cHK;@=cS zyayZzn@ew_isJ2O-|kEY9gfu(r&|s@X68|!rj~XiLRKRZntwOwoK~xt}hyH;qez4`jk353_OH9_SB&}L@U(|x)#{Bc&G;I6iWi= zOwAEqfAzgT1X3$6`9R6UXa$-R z%E-UwIeTktQ2J62W7M^NgPD_X@dyP+4(NCw<&AhjE-T>IuHGfeUiNLV1pjaTXWPtv9-o5?u5X$7uHE7cOS z+2cw7QR67g%yxi>_SZ&ZT}V-4A$8mQ0x-Dqln9p3NQ%2!Q8pnB)qWEujh&2Br3b9d z^RM||@O&PO_Hjs~8C6pJ;ipc3n*&&9iH7$vW#gZ}S}5BNJ9vsKFj7SYV>P*kw9TZ4 zWC4re&B)sd9a8aVgwOA6KU%BJ!j71vb+uQvF>O~Y)+-w{ru`O zzd|e^TWc=CuFHf}7vR4}V=sVdQ{veXPPcyROO*Hf_zb3G>dEt^lyIvfyz9m_b3fCG zEhHTt058{Zu$`8o$c#|V1s&`lsHTAW3yt}TI4a52<_2|BKFVUaEAYBpa(18UbLM(5 zOm?+;N+8CN+%c4>W?dB(k?D$z^q zu&$uQ$wlcFHO-z~38K-E>G;XPizgY%DIw+qYfb-v;?>#zU~t=}*heOIHUgb*KQ53n zq%gZFviI}2$`YLW$HVbb)wlpKvpcit%I*@!hdcOoIO@wKtPOQU>sBb#Oz)n-`Xx*^@dg&iCaL+RmH#N&l#X7ANt+T%PoA*{34md@it5TX5^3Hwt*jDGfdk+vboh1 zZwh+XDSjVqZ~0IXv`tJq(hUl=tNWP|pKi1VW(2rcGq|<&rEUQ*i`CAy-kkT#Rtfgz z%t#Tx$Sn>BzZB&KZ!CC2psLhEnI>BG7Du3RRpO%_W8{lgp?JzLXXmBi;Gd*3%|SX3 zMqd5(>kp?@7j?T4HYUgGefkVHBtfG;MpcX3MjWME0H*kEtcW?bwG$xv`M598 zL#)4tAPz{}reglw_Ok!OT`~bYlMCs|RU|ckg6re5yu{8+ga^C`bJBldJ}U7oBtq}u zzEtCGklRn@H=;OQzPrRZw^80(V@tlWXmWDO_cQC!Sk>vJY(em^D#&N9BZvzMCM!UJ z%0Pdv5UQRLYTB@uHd1c$ag3su{C6)!^s=8#YZ7(^C{)}zIM#uJTT#o9(OrbDNX+7j05Rs_7Jne29l}EZ-xzCVvW73gG z>?qGMz;q0$lhO5|`@s;3V)Glhs1f6wlOn{+x?ED^qnvw z->4?+$(V0xm$hcEO-$f=LMNS}$p1VYUri)2xGIIIifs@T>oN#u%={zz&3lW(+X?iE zt9`T~?mJp&-Jn*aM6N4>HnN|zBFq!A{dlhDZlAia$3h#eZrSVh#wa1O*!O4!Dt4!h z$e*xlLJY^$*m^hp)An0WTHkPfROClemtPhQ4R1y zLzH&Spt-uy2fUAR_xk2Bw#&NBwEmr}E73n|cEQpf;T$(gyH`J`Y|ZL=NaVlvH#=-HwBKB$k0y1Hs9;dDTvDk(B;qOT?;46`7^i-3 z#SZ6T{lKHaeX1@;h|)7XHsEf641H!w%NXuz1CdaVcl5?ApjOJwt!B|CafgDmVVea@ z<&?!7)c1+@Zi{Rn;?}sJHmc_TfXH-*LJUGgwvaF7-8N-L*Lg(82RKf^_BwXImko~r z*1{0TKwFMWpU9p@OeS*;_BZo9;<^$qgV7zB0go{(DCniUFk9KOX;|%)42MOi7nA~J zkwwD{IK(;$whJ31{|`v$F0b3K*U3&-b^1qkydMF(C4JX3w5_flPPfw17U_OJQ)O*L z)bsm58uc0J7O*9_;AcDu>|0Bz#R{C5U{+i1v@OUkCU+xs+4(%A#cgyf@*9H9r=-N! z8557&-h?Lw_O^g5N_&iOAF#bcNbck^OnB6RSVKc;hTdqquJB{{@r#Jv++JXjP6+4p zKGQ>EjWR3xJ??_u6CHxm|7-)4l0U*CTWhIn%`UMREv@$MY$5U2D4n}zq#t}cjqd^% z*1;XQ#-uw;L&D9}L3X$AB_o?WD_sA=B6BrDZ5W2DwY)Cw`nP$Beg&en?w_1>J&{kl z<)zP;95AXl5N{IZx!{b++sLlp5DVKw9i{NZjSMo;wRHD2>ssJ{nM*V-=J{^E(FTR; zYC{oN>y1`KC9Xu32y?h9Qtwav)ay$!zWy_%xUHq`Dz9A9`Vo?5=u!iA592y)&F;Yo zHt}sOvWMx$yq|5(TW%s)0@%xpjEt?jFU{k(QeMzxE(l&ek9>&#!k5wvYW|*qI7X4)(mc*PpOzkYJ;PyF-N zACUonMCvNp6jYllwYqD2JoHnuWK*S2=9c6-&EvCLHC^M5M(ZVa{HXYI!%ih(7FVw2 zfMP0`NZ%O^k{NZLjwXjH1d5Fp8jTxpi0ZT z7&8I`T?C61E_4}|;sT!lx!-7uqOmsK+w}QS6qGbJw@)M}ZhnSStD4t_JG)N?a(wS2 zQl(;(@;nub0Dz8drY4VU->piK23~%mxn*6#J6{t-aCLqN zdtb%_?xYrrw?_w@{m_}zGsxr#SIaR{kGw-gjr@BGo=oIkwU{N;~ws zMX08fslq^4a$SBvNJgkjO*Nq{oq>|V?<7K(JK5ihC6)e>Z~f><2_nr||1eA;zKHxy zrWNa4*sg6NJQ!;g#)qvMQCXu~9a&#e6WkB>Yy|dpdepA!S__B)GqqK8!XBIk9uD!dv6^YTSgdT1GQ(7gZG` zEuSlV*!E1@2d@NuxLWyFSQgnjN+{x}z~n@nBJPO#G*s2vYCCg0NpAX)E8Ul#_VAF? z&*pVBHW2L{1ASKR7S2>M<8ZT>;{s}*CkE!fHF9(o)je^J5> zFBp=8>g-};Y#H1WYhK&=LkU~8(9>CE2=M{GX&H*`GL~tM|8{GMSCBA5{P3cD<;dw<#(4rl3a&ONvUweyb6}Zbj@pjF1$Zdxu?{ zr#Cs#%P(RXZxn}uPqiOWyy2tQ>AR!LN?HNcz_tz8F=M_=bt$ZSDB90$w;wxYD^BtM zT@W@XCXLb|N7Q!-a-M%!551@Mplc~XOlWhHT7jNv3t$0BTL0n zwWfh1iV%CiOw&=oWIaS-*gGYdrr(zM4;%8Ee2K8$Hx5(T-43;`JNYRrCZye_V5OVM zaE{E4*O5W!k~?wi)`?!{3AqyOFnn!>---DLkG1B&WW3=9`#&IJDEp`8p57_dFA}+z zN_2NAVYdOWMw}<05QR2rLwhm38xv({V_*;kb@pZ~?1BzGk5jNP&L|%Zl%bvVF~AyZ zylz4nI;&D(E|Gw-?v7#87<1#7BpE__@d{1$#v-6_#dr$LfCB^7R1HZRUNU{$Y@TAC za#Y|S#}fq%sa*CCk4|#zRUGHL={H=5mx|(|jSblt+GpkHZ$HWyK4MyoW)yU1QQ`nR z@cO1;PWK`P;C-2ttb;_JXiqaq|7sWJ8|7>bJMKZuhzk^cT|EyIgv>rJ`pU;E)*MVU z9U)i!(SXgWmX%+{#y;n@=J`d5gy*esGg=$bWa;dyzBb@qn|f~FAx75Lk19;*ygSC$OdSdt5+o% z^GWd4ge#0x-Gkz6LhPXH`SZ&e=&$#8V*SWM(^b(9-3i_SAvM#<#+P zQ2uN<_{IA}uk2eHLX?g4&5|l9`+-CMbMwUi)l?ytK%*Gm-rj$td5baM$?Z|)?pJHV z$zH|RDlT0wxsmLs{Yx|?Y}%kq$X_=d<-o<=9#-(TmaLRB;!l`<8t>M9VShJ=00aZA&dR%Ul$rPlifRF;4j zJXMw4uj?BOep?r11^DXs^QIQd68to^lR_1xa*<)x>n^F^&xrkU*2C{uGA2=;sd?05 z+frpEK)ZW&9@3vEO&rXFZ!jCh#ni_ztQYsjWTbzxsqC0{OI*MU!=IAhyq|h3WMB3> zGKV=J`|i{)Y@Gt8Q|+Lm!_TSI0?haWT8?dB44SKvFmTNI3 zR&-2a>kwp+wE;V&DaQq< z%A1+R$o57SPp6b36QB&Gv=lD7yKO~hT!sq=d{De*XW$)Bj__ z)2(zJn*+PeDIJFEb(UbRcLW&5?XO})Xo&;eP@vb(C|cp%$%^`{pzu+0xphiJjolml zJ@7Le_xkq0B;hNWKVeKW>Z5}-`cFQODiZs2CmecAm(A>#7BoyVFjLC21|s@EIpyB`q=a&!GuXL zCGjT~tYv`xO7=`=8ZTARGIy|C%YYra9~fyQd3d`}<`hwio)Ntlzh6rjT2!3mcHFlE zvdcGfu8L6=ba>+}+U%hxZX_f85QcyIHv%|QVNwUZZzW4?n8$VLBiv5F?#jvC;}S^G@pzwiLWHIjqClp^-K2Y<39J z#TF60hL`J%=|s^fK_nk4c7nNJ<4Bz%Z`XFOdn|JV+?YG}2}SmPnC=UP-1%t?AMDg( zIvduNI}XUgRz1c#x7Ipftp`#|R~HRbeI5j9WbqIByvg9X-7A`p?ir>S19ER87e%SBJnJ^iU!YjE$sy~?!?RhI5=@~l~&vYUSGgE7f+eyv`2Ixz>0%(Q(v zExuLm;>>E-+#D18yTgQ33<1%7fE5|^PXf0Zu)nq>l8#jz)_Y3Ii0zF#SO*Mo?gpGf zUXPK(-pu3QZWGozo?I@hx{Ac&5S^;Tsr zjY3lBPaL&>BYw);sdW1aB87q_O<+^z3h2HISNhBc{bc=MB8W%sCoOlS3Y<+<(Ysc7m@4#chcA5;^PaooRh0tiKdf zg)eb34enZ8Ye!469=1Fc+@&gasjb(uUM z4w3sxed|#ozeSE{!8bEtTG_pD>i(v$Q+-bEBQ=o&FgFi>6_O1RDB~#EXTisPsKvbLN{#wZ0Jy(pSx)fPk6cH7-NaeRa5@PMg zYeVC)Mc{uxu~{3o5~#K4zFv8ANtoL?z(WT_&h4OIQ z%s7^FcUE_OPxGE(y@a!NOq~(`1_xd*Z~D~OlDloyl@_#+iFEP(oc5TNgxn;UNRwL= zZ-ITcb~}vg7bN77hT8(Mj*3NEs{9khl@=EHH#Bl5MT3*^NN@yH2z71y#=pS)X-mh4 z6fNOps{IOm-ngSkuqNIB$6o;r%+$d?XFIH_Fn;w}Hr&}iiOYrHsxN(JzQ_8yB;4hQ z+1hf)!*xULHNN8DW4cdJ=PSw-#RDLU^B6BDva(~QaQ_PDn5GOJu7%_b21~?=hwOgMwdtyMI91`qfDO`~yOHO~zM(1&;O56RSkh#S{9)G&#)@33lQ*eZ-&K@cwX0 zTilY?@n+@$o7>4t%Yruj6UNM}*JM9}7xsTwBjC`WYRJL}%M|{BY%O1~|F=cuCSFn- zsxLggVOGD@$)QH^>pH2`xNx+19%tK;xSf1+!nn=o)jOrp^^}$ws8HM7jS$qM1(#l7 zsqI|6`1t6Ui2m{C;eLaX2|ZpI=&}_Y!$s7Dt-&RGRkEIasJ|JV7X6H+m*2=*X^TF{ zJ?TYK^xtsLgt6j^z)i8*+lwC(m-XK@fRN-oFFW?a2_W5J!fi=g^GD2&u2|ccUansk z{aEV^%$iZ$P=D9kY#4i3gK4sEoO$_7WJ!_plhJ*m0vxx42iiDhJ|Osa&95hq9_c^z zp}VtekM0uOxRngd*?arV!63OamJlaW`y5 zj|o>^w~)=-v*dZ{r$@~I7+{tsO{BX8Z^z&fd-m6k{MN)S@sqB>`fAT-%n}e*FUJy#BYi= zM4DV5uJk-|fM#YISx(olW(Oa&#C}xnd4#V@nmyE8micLKE&>ReaCxNIa4Kq&4lmy| zb>M9?+!jdxSr@*U{*HAxXserMkZDyK7l@hED|$Tb>93P`u!<5t9%Car{!O@%H8N(W zizV7%ip1t7;486*4ZzMlq8a`CMQWxo-`tj{^Y}I7$Snrr!#3`ppLOA;*rfI;A8?!QTwY1^-inD@B z@CDhLByYRiDBiahE6`RC0rzuprkBmMu*bLc*pI_sz7url{}T)YRbkls*_^M>+pyFW zehwi){wmg;Oy1(;v+@&0^Lq=7Xe&~N3X5UdN63(@cCQ`HJ?3wLnn&?Bl=w{-j_GNZ`2-Lc(&L;zLC zT~&GH@<+CIba*@plpgK-9nHm=@J9rgy}DYfd7EO*I?>%xR76j^LXub_g>Q8wy(5wN zx-rx83i@PP&Y&pi^2MNOUF%otCpk*I#7;mr3s)mLHO7tu=r9JdaV#F`K^bnzcO$a> zi62Y;LiTYL^ds`NW|}8Cq2Wm;7#q7sY_?GY;wAK~C1RAla$mULYJ{8G8Xr8s#=pR@ zx%be`Sn0i-CoVa>yD9*G^gJ0}PLRJ26h%xX72Os)8i{GXFL)I?sDpOrYx!lRgJj8O znE?LRx%(N2_hk-wAI~IGzSh`SYKa;$$nSIZ6K@uFLmF<6p^Z}gH$-}G6+8O7jTY}~ z2>BFJd(1dVtCT(pNbdTyz7?UN(jx>F!;Hk-2tY)vXlR2dpD(lbH`fpB6$QJ4Qb(@Yt!JVB>+}xZ9G@0FX!VYj{+=JO z;y+3tpKVaLU}gs9FT-NVu3ijv(a{lIWP13<1TYYcLna2avEjp4QtR{PJ=5Z%RUHtE zvG)0r-KxRh!ftJU&LP*t6TyEF|r#`sPL5Cq|S_eVQ8Rfxc7tH)QD0dGe?9z0seL20F^JbNb*z0!j2%M7Z%P6tk3G0gU?Gl9ghyD zq)1r#_0uA3jh_M$V#P_M0g5>ms*dDN2>bq8PNcwYHk*crM}_ICvvC30(~eb$kfq_+ zReo#712-lq>gsOYGPB>Vd>*B2o_;gObdiJPjz2h^lm5Zg=yv!cKjzkFf`&b`1Y8xo zz9L~X2i0)!3Us=0=yY!jiezR%GQf*~uA>%g_MR4YzN!k(H+3))CnmbeN>ai5;U)O9 z{vf{*^)?ac5Wn{`{FO@HlG-!W-Tf1f#rlir;F)V&El0f!a$qLB@(f<7rYGIG&cfQ}Xttpt+Q5(V zbMiQLJ$Zr@#0EllsR3o`OG5!xKnpH~AF8@sM@VjY=y@geJnMr2E>gMk`X)Lyv$V&Y zx;tb$uaiaHHaRR<=vT$Tbk*a z4kMrXipjO4Gun}eQHDRBU|FFq{3z&uc#Z#;3^1`;ZD%93XI~e(JN3ain6`@%kzoX3 zlQ$$-r&Xl|Re#i9uL`!a=|TQ%w~Lb7T2yt#@JwW{mMz^Q;Xl;DTpPPD>}U49OgCEF z^vrZtQ4IG3Z?GfJfRZPDiGf}!03<%#;)qICM1Bwxu+3XpJjsmg%A8#oM;;S}GXcwQ_$ zY9wJnyYRx$A>bn8zHoN00E6By#bskok^K-b31H-uVysMdy*9a^A(*uK zo1_gKA6130j>8>+si#5rrn&b^kvHyKWUtkqf*nRDmLq}?Q=AF|VH{KXUfc$)jgA>l zq(p-zZ4QX)1?o`<>b>%CFZb+)@5-W6q7kl$RgwsG2#ym~M1ovhTZM!VWVoMi1>Lq~ zEzeLQAfi+Nh}Mq})m20-OYyn)c$JJsz6aSr2&gfYC(o;7S1S`L=a5f^J3W&6T~d zix6VzJFx5sk5au`dEgPsHZ%S#?dBl~=P(&yZX6DGJtBFP%_dNb4h;}&ob7tHIVtN~ zVy5#1^I)UbUZe`Jp4Iz}+^|MJ#`M&+J{wYX{_v8Xu9`)1Sl0%2{<8MdJt~+oK_B&9cGrDlI>RhZuF)prw?0t~qPd5rAMFty zZ^eqWy+8_hsDx*gTEMpCHi$T>f@O2PD#p28W_g(#FP{*lgeDt_Ihd!8nWR6;W0I7N zpnebR1|9Cyc`(4N-A7+X3&b5g(RMPaSZw^`zMTtVZh?ZYUTAM5O1u?<-Yv6zmGP(v ze~$edC7Lyiic;FH4=q+i{lSt|^~!^OJB zTeJTGb*I0Vt~QOF1pw;I5_-ZSN2{`_^^ z?oO%B+a)+hTF_R6db`t?+(%aoMXswdJiGzyYic5!fM71%jTLXKfGT5JzW?A{nWbF~ zdf;?#)jPv*fR(7Qc;O=ijE*a5fIQqDRF16+SRikrp!@W!%P&8FAtP*Zm0=dITfc@k z#LC_W42s#~?l#f^S$yk9T9a&K%DD+HD@w%Kk_ zSs1dC7?DU`Wx^b+#j2{UQ$4ep=FiZZdD;dKy3@Des?p&Y1AdA3wx8MlHgha^{hoPhz>638tWY z%~P^#F*m$(bw~HQ=MTk9R!7(!CKlQ4Me-%MBFwF?1T3u=chvdb3#R1+=YLl8K9coV zUQoJHgAjpdow3HSmRd+%U6(!sAreWH5dY3X@|W&dVM`xG3+;`U2~X_OLKSfhAMaXq zpAGFx>m>W!V^ZPOBp@&snP~y}E0@!tdXX|W{BCBe$JUBt)DM$C>MQ*Sl|xtG!z6dF z!DVS^etdidTk7>4w=zpZC2&h}hTQ%;us)v*IJ1iw{ej+Io(I%$>{3exFPBryf!FBY z5u>@14RCUp6wZ2M?3b#mXAvfGO!Wsx+TYA4jZtQj^mlcPr0)}rs&hb&1~pjK?^^xo zWmQ%1!(1MJN6WXi&Z+D3L!1f4dPRd)l{nb0WwM>&*;jjl!T8e!s7+C`Lb9w7p4Nrl zXvSOYwcYU}Nn6Rph_`&(!ZLk`nieoDn~fX(HXQV@6uq@M#*ut4<3V!Zn+G@V2==3t z%%mCcsk4srfQKhFx=X!~ZvIHDe!$LV;HJrsGln+GToGXxGWd?SO!65#W%cYEUhny= zDt+zlJVjC>O1K`*u^CuQLw5b%YpVxS+2+C7r}wf4vMN@5)=B;uLi{?fX8~12ii3NkB~%&e=)i|5m`vH$8_)Rsz;|&~ z8(x#i>Y{7b&_|KzpUvQVD4s~JM>)bowPhIqeBM&vga1UBQnX%Nvp|8FlOYT7(4J^unZ0=Pu zDhW#mpIF@};=XJg#T4B^UJbP}0X+jSeNnhz{CM{IFbTnZGS+47ptxQ%U^hhSqUCb{ zTbHZ=C)?O)p=G{Y*U)+z^AnD!a)k{|Zfm>_CO%#O)7pl3aQlb9ADQ|am%C&7S=T0*Qaz<6>RgSyQ0?8YD#!VeI*nbPS6F6cZ{{hngXb#{jdPWXlIrmsF$c!`Z9B zY>~UHFLmcTiz1LQH&VC~2{_*W+#jQK!tFzfKxm$ zq!{vN+~I*pAf=()VCV9;mS2u{AD~-R<$P-Jj<}05$ijdmm4;@kL5WPUq`pv=WqN^; z?=n0W)OFc%0rZn89jCbqC8pH84-s@^g zXPok;f!k|K(myD9K{YQKuJ`TzfGMaK7T?2!&2?W@#+=2~tnZY?;ur5K$ozdjhL1Vu zv)O=Q$FiGnQybyb7I>*CueYhU@4lDOqFLmZ`ke9M!SOcl3=DKlf`PZuL;MVLar*&n z2@Y}O-%~5z5|Q&hDm&cB9sksN#Szd!`*L`d_-?A;eJS{*S%&&Cw2#RfO;pu-pYZX2 zWzG5bvxnAqiJdR7>B#J|EVxf|WWo|4_~ZqBR&0T%Kc=er%_Q8vSagKUem9R;5 z<$2j&h6e>8ZPupaci+F7U2hf%qs$Senr!MMFmGb0HCDc$Yb%?9dvAT6dUTw{_c{#^`Y&f`ZB0}ZwU0Z zh&*?DV2uSZ1yK+>Hg$DBO7tlba@0}W=DYF#!<-9Dt+?ZZPpJa8yZ75f*g;|Iqo7MT+o?tPgF;`!(h?Zox$1Yz8YsY*V=)7J0W38d+(X4rHI z{*b;COZFgpcRoV(WZiY(n!*If&u7)F;q7HdVvu#~UAv_#R!vl=S)AP z>!8V1R)?2snd=hsMhSLMG5E2T+tUcI$8m6^0Dy7C$Ql*SccDKw{OcOIUQ&b2qn}7w zPp9-Juj_unGpv7;c8A2nq2M~GBF-bc_7!bn3k2ORm71$9+StUycgH7v56poV$D!60 zi6CoeQFCT>-r3e4ba{@l0p3xZUW0#l|B7}#lzeSVKr*;wcc@kAUy zgj*_OjCbv<(czpHc+FW=MpU(I-1Ku}X>H$WZ<|l7ihUDOdv^U;tiMXPpf0v0W%pD= zdbYbP^EY?bZ#Z~$X*m4$34#%K)eo3CWq+s)a8t{=)%ic`=5M$5Zqk2c&2F{&U@CS9XB8)0v4kVy@mOK#iJK|j0b_E~~$;+d(=lTl*gli>97FHx&3 z_?4ke&-<({YTx#3-&T(atrAwTTlhQfgjWA2NB_SVv=kosZC9Jy&W>fn@JvvQx)x zV>XO?r(C*c$~)UF4{7stK8`W-)v)aDfgeIb&VzB!P~9#n*YrAf(GM%|fAdL>F7jdL zx-)hBUktu@Z3LAnCBveo3V$1W&Xt2jGh!Nocmn^GatrDoyj53PwyS_=66em9rTaCf zCCJ#lz*t}I%#61F_o>m zdBFT-=777vR)*_~zb|}AC6-`kMe*S*;ix+}tqM!y5`E@`1D<5{zG;Ft<2Hxvw~IQe z1XHrk4I5G`Eoxw2=T+Q{EjBc({T6tFFX}EZ;V~6mH$QWoQff|G>)^Qk;G^__G$7G{Zvl=As8))2>v!EQ_^S&kvKUyWzrFqPf{1VGBl>rB|Xe zlI|}%vSO5mDL^3`0NS$hb5nck72hXb?HP9y9Vxiu^ykx~+c;PK58|>vgy-j>W>MK3VV(r9keC%Fl3v3bazgi zMa%oG^9$GKe|7J(M4xbFoxQE*&l!KId2@ux+)K3@n#W54A(MpRwkqq@_j$e_N-e2_ z!|wpa=HN&#!G$ojUwd4nGyF5EQV!?84!3M3DW|2|-rYD{ec6|p@HqHeZe4C26Em7a z`Yn?*qTri)7E?~~&Lt0d+2VjR?bXy*Si&YHVZY`##!5%i!SGJwLj(m-9z=OVjPW15 zfM2@ONs0-+$2l%_Ll4kLFyLS zARllG%7=B0Ka$wMC2n+T_6W?rCxN*`YiLoSjTzAt->XN)zyTKH$yMUTy|RHQ7m6M3 zG?}$}m$nG}o$2xs&Bha6mFUny)y)uehnqtjv4vyuBR$EQ)GW;%(Ecruh5xUBdno19 zdg*i`VNX%(KkjY>{R|jT0^`L%sDIQB*SuK@c^PGrr8%%@`2qVYsSz*kR zPlIF)?r$WEArRc(Qg$~T?&@8nJU-mcobC=ke>yGK%_hOBRde60eZil#1e75!Y1?a* zwOz=At0#?D^{=+X`(s)Q!b^Vr^>%(Mmi+r$+Q+8s)i;vlAQ(`LoUq2=Zy0#~4l{_< zj_$7@^NzNS;u4q$C|C+lYLmNk*NE-nt^AXLn>b%_Y=_;Fwo!#MKGnz*6a-PaRzP(z z7kb>R>OzZhd1v!O1|wGO^S2>nfPY zmv?%f|I*cpMtCVIauTo3A{fRhBE|9tb|`24U`Z!}x&TaG5#Z^(9T z{gtUTUi;HLR36G+va&a&E0-@S_HymF@X=T!a#@1)a;_+5jVp`^_sUkJU!Ci7w#r}A z>@7d{#w|#{{j~Ci50Pr_DNQPHdMjA3m~H_~o4=52?iu<-E9^OMcIioiZVRVpfPmz( zIyQW84Gzqs^jaoq$&Z?q$bD$rQW)Fx`c^s&V+sbh1uH`1OFVu;;s=zylRTwF7t$X7 z1G-D~>2V1x-)A^f1ZAA?4~V@kOl?;RqR=-Vhib?|eP}3ggZ+KK)SRJ<$zMPg5@4>^ zVxbgeyY@srjNKg_uA9Uh%DSDB~YrgG_`8S|{J zo|5z30olnuUH9Uv-t-@G$J^j$(1sEyk|Z~|=TrWiMxfziXLv0`mhVI9jVg}dg-X0# zQ&$Jh&@HXmgB{d_icZRV=ZKa78|~Cb~^s6*+))T{lYK2w4hf zoqa9Vu&T|M{gjVPn%QlqCrb{Idc(SBWtLM6@$2g_JWAw|9Fn%({d3m@M*9KWpmln_ z)g29px_IqS%u6qdCo^86EqT`NSqJNX#pVhkP09mC1Xm-ZphCBb`qm=MxWwZ8b1yCG zvs@;oSPB_f?zRy{Pd>(avEUpBi)i?9>A;l0%6|k_9_Q{=u0opuvCNj{H6t6m#o@Kp zojMRmx}niGhv8{`KKC}7fo|B`^U1l7z)AK*(9vp_HR=L>!wPxUkHB0FuTszC4axg_ zuVwos`YGu17hoBHgfKc}YH~HLAHmc`u<`w&Z(cF4KDm6J-8!}QxhhA(cvU!pgdLa@ zwd7sNL&dO_WhjS@@B}UaF3~wJfowgOlwa0_4)#;k78BFDMp?UoY_@aT{UP6Sv_4Hg z1%H+`u`EteVo1|czD${0y_swcZcW9+SpA^m#};l5N8n>q8yY`r zZhwb+Y7i9LU6=C6{cig0toXwy`iSt`psRrYhWSPKzxx>xk8jBH*GZx@N{aB5qU+$y zvubhPW0dr39>uHZLpadF_jm8SbfLwN6y>|J3wJybKX6b4;TurKrn?qM;He6yM21ZU z7bL@9nm2yx%wRHm`C`du(k@7CZz@ayCx!P~5C1fHa8(s&5)n~RrV{snq{p!nw|+ z-%d8vd#yIl5Xtjga+p-Sh%Fl{->jV4c~elM^v4n9_s8sfu7d5wG*562j&-dnuPv83KiPSeH}<+=K(hoGhHgc)etS?-h5Lv zMZRM8dza!4CMB!Tehw+;Kg7%UbntD~^;q6FY`86-5&|8NpJrZX=vUUd= z>WCd1?71_ACvf4|MZyeZ+h|$rWF6Xk=?fq1f?t5$!em?Ly3|}9B=TIn-Jbq-dbd7W z8}UX_!wQu26lc1Agfniq5%fq&t*$6{o0jQF$e?f68wgmY3^tF0h_9#`Vy92Qn9@+# zf?nYRALW}%HLV{pv2O)qC?<_s(*LbxDtd^$RWwkmB|PNEckleI`H5lkdTm7C`o#6(VrW%+q=tlE4%PQ zW|H=V*CR$i>7Ho&-A~K-%7C0G83i%AB!xak5NtLcN*c zc&{2*7T(3rf=~Y~ z=XC1-hPk_(kGlEG4-mssCZd|kM23R0dXPGdhLoZF&Koy^{{zde*!PX{wA5|jW48)k ziuTiT8>v13$*o5%^ZOGsM2b)^7xIY}<$Tq_*e7oHU@>2gd z_TD-wj%{1}ZY;RFdvJFr!QCZ5fCP7UhXew_H8??nL(nF{wQ-jOcY-$%pwYaw_c>S2 z9sBHa-|vq1yXTH^|6p`eRXw`9dabIt=6vS!%OUaZf9_bd_4UqEYctqyqSh$cb<>JF zwnjd{O*@YPqlms8By==?-ZLd`-t&F%RbIPa#Z{JdcIozhV&TbQD5eq82lKe1N$mpN zonW&P5HCZVCA&HfylY6i_7kiG4|3!JP+_DY`bl@x6v#cM~gru3f-Jbn6B)@excr~LHWn{Rn;;Q-9;lXxeG zrCCNSoF=)L@wZ=~89I)-den7G&+;K@`N341@=~}c5f3n>m~|-WE-$Ba7bS~o0H{U6 zIAJ0SC;fFj2A4eo-%uSa7E1~+4*RWpRDu7wtbyM06lI^+L^CV*)N-g`P?p=Zj9b!L zOtP$<{Nyf02;QaSK=#fl>p4Z6j6K+0LJg#qpLYh8i*l?>+ zofJe$4;Jtgb7XvU_g(aV{ZUUBqleq$UZM=HTQe>OU11^NG{_DESA*R6CFgd2xTwz5 zyKNecD+Of(#_hPe&`+T>F(?*pPWFO*5tjt{4Cg_Go9s6syc9kqF1*atL!?RhQeowk z;$u@M%%OoTr+JMa7y?5J!)r~G0PAui`9rG~ZaZQ(S9y278A1!sG9CNKZ-N)Hb?}IZ z`2d;DpeTS0G^(OPvVEsk(Z^{95E8vLjAnHew}~U4@D2)6m(G+3BF5Hbt{WC#YAx{Z zC}F%^wEeF;oc}W3$6uzZ)#eHR2wxvXMyps`+sTFvED4 zux=U|iqbX~G3G{pZO)CUq}VR`L*vz?_U{B1c-*8I2WDN8LyeLx06>o=X#yoV$#4~^ zN}oBB!T#J;H$lDZ4tM+Dpoj;-;AV;B?w_RQx~mbLM0gsbM29=jY785}2p3z|6}&PE zq!-jb#E*{)b6HPwiP3_uhr zI2zrCuf5F`&$qmvXpGG4>#L{{gl*oW#v6!RUY0)O@h6pp%aBO*v|%yB<5jz6SLZx) zZKGU%@GUE;YyN6!SERJc*XpVjQ^#NtnGh+SEV|dOl23!OE-m9~{IRSWuSr3e^!>dWPZ`b@RaC(uD21p^+3Dw+2=|}n#%8^FV=|s zS$gdj^I+~}FD`p=ZinX5gv)R{(Sb{S@7wJ!$P3kyBwG>JiXon&SiR6svf*-xCh@V} zdwF7-2n^wicCqed98X9kKYh$~YU^wH+O#@VGdcPMbZj zKmRHD6P01t$gcn?ZJ;%B!NE?OfZ0U?aPV=fN%5K5x-lC9GT&DNsNf&gvBh_Zb=lA?Bh_&97rr0{u!->S^w(sk*Vp z6qeZYA5(m(t=~Asp0#d&IxNihtve8E@{qp7b3ft4O+}4d@MlOh@V$^p>x!)iG$`-G2}py{*Jv19K*Ca z91yl}QOQD(#GevPnmO)G-8!b$Bs*>K?mJKXcYZi{ayxXBo^C?F*ASY$5}gd0`Yeq` z)1H*KH{E&&rF4z(g{(_eXcjJ=FwYX-MLkH?RD8CAswN`$aNBZ)V^}L(7ktvO_9?F_ zeM?mRTf_u)yG<-2%sv_7XWuWr#RN#BVxoTlJtD4C`-5tCSdm`fAWfXiVzx*{8KwJH zpKRQTPgTYJkYdqXBiLat_#&X}hEl=4dotl@cMU1xIdc&w*2pp}R;R*{c=K_1vW zqsINsu${;p6#4RkhIa@ai6DYF0%=DSlK6N9{&_^s`B=4+SvaKK{LQ;%EA^w9+ruGJu)0*#H%+7$M;s&qRg*XsBI@+5qDwDF;&`J>a`je=_mHJ8+eeotaJF_kTK z-->T3fe)R0W+axkx8y8H`Zbu*-10mlE=GhZyy0=;l@rtVtp>g*@y=BQpWq1T9NG@$w1KsJTmO!tQaVtxk0DuY1Z)!MCaJTNqKzd*PB&* zgh@IsnWP8Cu;A3`uxsj;Z^D4zp0&yT8FALOG4j_p@Z`2)37;#G42umm=@{kEbg}#n z`_<4UFk3v>lvpn!)(-5aMMx6!Kg1h=`QcI*o%8w64{*CG#X~WYa-4p+88D{4SW@uB zANNg#$B#{cC}(lkjnNKR(12J4bar|3=q+-*f!5=;B#19A*<5(#lbbJ}BuY{Sp%1x? z@>0ANu{9F*eNOI!rqA`hkRZo;mxI{QNd|_D%PEzv`Qa$oEJleolt8(;8asM;1CuNL z0)5I>6GNc{<=61lTSrF>;4sy=ULl!?`5be!X?2R$4xaoC`pHR9H61F^Q8d<80|JR@ zPnu7*@7M%pN~mw~q6J`)R^xovs-u z$baCLet6b!75NL4yaP}lZ-J>Tr(jm^`Jc8v*ZbW%-QeKO+MzMD0Hy3t95Vi9dG^=i zP#Da;1W5FBxiv0Y-TIX#oSR68`>#SRpXP*ND+Y>di33zRaF#yr zqF+Nu2q?jsO;*r+1JRvl2w|c9dEj@_qM>N%jfL_8WEmXY^JV4aZSe+4IE zfMz7xFke=)1sceJy(?&IKJgxcP2w)f16(7D=gB<5m12hX`?)nwkR|-}G9uZ&crVMf z6S!hGsj9xG(i(C(xhiHqFPJ#_K)rD)O^C1B*km0f$PoUb#>42V3wL6@rIQG46q&YW(y-P+r>gNP0lqJ`wNUdB?pS;0r^g7la!Ik2^X@$0Sx_?8N4oF&lpY)JfV79HSvkj7oHS_Hf|%6&5F1 zye0O!u1TWT71guUMoo0lgjPv1Ut=95L`svwr{fC(%kM=9;7lDzpT_n}4k`TtnN#RY z(HQnOf}WUtcuQH9FrL}hAo3<*!!(M&qV+>_H@@d`yW%x~l>GZPI&yytT-brPJm22T zRu^+i>MlTuO>nuZZcUwI;a@`b%%s)JmZGIE!g@1b<_6vc<@<*YFzPQ42-3%fpk5Nd z8F{9W(uKK+P;~INd2Ov0<*POtr+PJG=!Tal$X^P$x+LSH*U6gzHnM#HD>Y7RG<%q3 z5==TlFYyDe;%iv;lV|a3T*?j{4J(h^5t7S12ZDQfl8t`u{pg6fE8`;kep-Fmg<88{ zq^~&Go%5lrOGXf+%Y`(Drfs&nt^zD%`?O=^@2jg*bBs2tOOZ#nQ2Rb>!*yTubQ;&a zQ!-08l#_I)zPJw}m;7-0tMh}I3^9bVIZlWmDI=#*+`jPD)u#+wTsdVB*xcZx{Hpnn zkez&J?aZMHS)=NnTau+n_Ya9O_^PBOji9|jdfTheDru2dOZ>)*i-thz<_(KGFy5{z zaTa9=0KHv!Zm(n$U(T_-7M%}ZwQUe)sfZ4_i1J7jS)U0n6go*UFU1*M!nt74q+g4$ZyFUfYKv3-p_g=1;Rf)Bia8!;>WqyhD!eK|zfz z2)BmU9%SwP?E}tTyxV1kXkMjD$>Nxg`-VjrdUFtgh+TW zUZH{^%CqU}2@lLlHz31?)OjIZqr{RR3Ly|%J!Ohdz%*R0e6twH%(+yMJAf<0Bt== zDb1NnHmk2QmUulYC|8xO8N!2q`_ca}6!@d?;7_)pw_ss%US5`sTE)vTr7Gx1;}Id3 zFbWvBh>VH;^_z@~P61IzCUU5q$8XU`(qBRlljgh9L*0WN@CuMSt^2ck)UZ^u#(gqi zVDdQ){20dQM*aEp{=FpAFHr5E)Xx_+Yga?S-RgP%6<#xdOJS&?zXHr~lWczDf7~EH zz6XBZzZ>@0NUgb)SCjqDn32W+`sDu8ppgIOV-Wz%hl94R>n~8HQS!eHm_N{_NdYGV zShC%4<}v>Q`2&6PO0=~LD^ic*9G!>BAwTFkX*&OGcPPgc0FeHpH0bYOnVIt6R&cfm zg8!~4^Y05Z|CG=F=SD|=6fyn(wJrTGU>P81Fn4eN@He;DkY6+Z_+ssV_k{SA3VHrZ zEgtN>5;X3^?4_TfD7<$$i3IXy5cHIl!46qFCL_?YWbZ8tS+o`c6jLJ!)i2`WQ)4|Rz>2}Ix2ovXIW9?`Sn z3HP>Yxhn`W`y_?l!SN#m$BEwWeQ)r+s5u>Wwr$stz>Q6r9oS!@e?FnCM{SmmHwPN5 z69JhuSY2zV?OmY@!CHi6qh+O*fG=0hMV8j6OF%&(xP5W>?wRZHEw|I5!`@z|1IYqF zle+cI2Xm}h*fog^Ri^mAq8$)OQo9eZ32Hu~_>L=SD<02GCyg)rtn`zM$BKw77X#51 zU?$P7KhCvS+E=7ER~$p~(>C)}a-dx6xasqh*v*bCSmovf7C2l$i#(cV~$Hy{O;);1H!nM6PPfB?%NZd>=tSphop(w$`XoABx|w z>^A$21IDxp4rd;{{!wAn1B-S(P4a+yA^B9M3?U5_3i2n1?v01~d*UiPK}kAjOzh-S2!E-JFlOhT-FM~G_bOz!aHbR7}Dg+1-W1NYI|31!6_Z)7?|CO`5H?D zYKNl$!DyS(cU5v7Mdv4uIMX8Z47;JC4+d8a&}~uogIlb$GP8=e7lD-_{Pa9Oyus9Q z0m#4t>wO0q?CnN$pjK(LdHk&6IeP&uJiGH2(|jA(mE{!wA<&Oi!y!s*=X|aB!rhSP zo2$FSP#~RkBg|zh+YD=DJ9XxgNLQ;R60*7nIFK$jbuLak2RedVJadV4I$~DN_>+uo zq9>le5#%wG_C<#*QukqUJ>qfDF1bgL_kF`?m1ZDBTnBzxf`^;}Sh-Zsov-IY=5}ki zl*qh*c=)%${s4`mA!w^E%*U&OQspICX4{u9E#`E%M}9AqbcS|-PEJ|amR?%}RK2w7 zhppOl&kHTcM53!O#X{E_qBzI82IZmAPi3itcmWfr#R!cM;tJV9` z#FB)8v}efO$2QutP6B=cL8yghEDT=)GN0*ufpt+87=+qMH9l$QI!1OYS^T!fuetnk z0JqTn4QK^y5p(p2<(mR@jZ3nb-SeCYy<6(@CVGwW(oYGEJ2Qeh6$6STI)57Btn*0b z7PI^|b|hF|Jm{?M^Ruf0Y$sUu*>Aq#n8#JtZPS-*ciA_11S*Q+EAa=0#Gr8Ec(qr) z=vPTo;(D8g_iSZ`FZaK`oxydv`Vg4vRPmzWOTE8$jm#$U0I3dS3dYpqkG|?~yhF+q zW0plyET;ebjrm(gg9zBB3z5v(RyX<)A{Z~QUz>{3e+(gAL_#g5@T4>DF% zZ)D-yfCb2{OSco79_($^!9w2Cn5+4NU7PfgVVWp&@9?g@9q~!z&-c93;tnVQodj!( zFAEUM8`E-}5phJWs!roN@lFFti_oz#-)0-nrezVheSwS1pbe#o$Mm)7t@bg2Ih^J9 zQKNHS(ub7pFKQp)K$)1`&i^K!gue3W7DH@*kcz_RdF`M>M8)DE8@0B*R|HSPT%o=HTx(Wq&L&iD{HQ3p*$cH2~X%y92T5MbG+XUVi@OpDIbW7|PB93)im38}Gr$$X{}ceK8> zz|hAH#-v6wA!r*msh$Klch_{wvi!0j1*MCU8u?;}bS zayZGQ`5E5~DwkjrFV0H)WQt)HF5jvwV*E;6WgS0;f30Mp`8=U~kxvA3>6OB0a&W;8 zl&mvsdr?`WW;w;j{~pba8)YYqoG(R}{}+fjKXT0$Q2th-#`N%o_^=9L)O*k+c5USW z{Hm*fXzQFF6kL(*_%?q%oh$Ezqp@93b*tpotpV4%20>{oKP6wE{?rYQ8r2o)vdAFC zBjG&%?tvMvSYjQP;FfrTKd9-qmBRmmjQ=mwYX3e+{3iso{f}67{V&M)|HJ*`Pg6Xz z|9I8l4%xNzfv>+5a5-8kVqr30$MT;q{B-`rEhfq}c09S}CqsZH@+Ks@ZT1Xv+g(^*lsfI#FRbRO!IoKSi48 z%1JV}T5eyIUcY9A0f?RKi_Oilnw{i`83PyECO_n`gZ*k7PFWR7rW%yef9J3>8Ese!=Gcg-y`Y2jnOON7x|YC-x8v>-4zq0*1^t^Y zSSGgkdlb@pOzV&&x^MwFzTM`Q|u zBNmzVJcqKSi&D52r}ZJ7bF(?Wmkbb-1*lBq5i1*&vQwa0#E>%GF`BgKY*26r z(?;T@NhXCySRyUJrfh_XY^iAoSF`oi*@(;s zD&mm}8>74L#=RfB?T3K71_-p-pj^G= z)JS%s@ku2r>`C_8s1(D)hJ++N>sV}TU29MHxllPvOUsit*R&!H*T$K*qty+ArcCEw z6yC+Dri`Xqg6Z>8;m+n3jz&FS>HwVtZyfp-=i+M0X)G{=!kITvgbmYlktp)meqcv+ zVmSpzO;_xBHP{qqOcfb3ctI%3psg6dLff_U3)LK6G$+Z;9-JRQeUQ?eJobtc zd`Wcg5;j}Tg!DPMAYr;gKID6)0d$}?KAEr=7JwGlhrHSG1Bu)j7O;KA3 z-xpLD9-rEE;`3Jn-$K1}CfO5bas2br&>nO^4n0|Ve=JCfjOgZ5U~M(fm>SAaJ^|>< zEnrc1Vn(p(+ljtr;st;GCiDS@^l_`OJm#=2YxI`bb9slX#)q^cfV7pdg=uCo$5q?d zORKo|^#$G2XAv#DdzZB}+Xg8kb_|O$@a|%|v5$@ipD8Rv01JTa+C>)=Xj6U!JwK{L?KhRuT*kxLExhHz2p-B?vMi;>G|69 z6JOENCe#FF{i$hj0YZVehnYMKgqPkA`LIs;3+tEqcIm-xwt z!mBit;6#EMp)Z&;5D%KrE8N5g{rOaAmLX5O-vUV$#=xRy0vFHr5eg19Q25fG_jt9Z z8GmPMTVshg@G<0Tv0L8d{&>%`H;0N9r;ja@$oYqBSmKoBiz@K8-YN{kx`P~Bc&}Q7 zoth$`0RZ9S5gYbWTx28LYccy;g|0MfW4W0$$$$pG-~U3MAb7t*Shd5$FRDgN6FJK^ zACv!3gUH0U=ESbnr8rqWx`x#9PIM5(@=a2zOkU^$(pE^=y#L-io4IPv6Q}KtXs2Dp zV%=ECxbM>J-NNKFa?H!&AiHCVX>QLz+=Up>o;b@!fD|&!Oapq^AuGt^XPzv?Js$iT zgj`$V$y;Tri8D}OBaWeC9al_07Pz%ok`!x;NBftW0owmU$^R?t&wol!pZ_=+|0)0f zCnMedaZ>(|67~O$+y2zMd+r}cf6w`rYb01uTsXC^GwWh!$_kJ%Bh6pJNf&sYsi!vf<^#V;#NrO0k zu=KxoT>*|oh;Y1%pt&rw#4^JTEkEv8k^|AA@iY-HZ)JJVQR&6oF&BWCA){(S$P&J# zTGA-TG{OlbBB3;d;@8E6q4;hJJ#(=`m4OI|zGo{~lb z?PRCK!|f|B_rHN-xTFYf)RIPfencv^wldpSXH?(Q+2 z{8tM2e_@UH+jVB3KhLRDK555j=+#M1N>jqve|FSpPLt&H{tuI)j+_nIuK)SQMn@4Rs0CzhBC`^7= znHUGnTvm9wFP#9kX@-S=zj}tSL%qcGsDu{sO?l}TNIJPC;hWE+Tt5CU(84zLE&GDY z9pFAYM5}R!Y8>(d&j9xhD5MWI0P>Uj2ta<41-eWBd*!8u{8RAkeBAGKyn-kHjU)XJ zOJslYdHyGBFaM-g`(L>={ik1R;Pkk0JIC7Ua*aJ7%JgUB?hlkqUa;?XG9g3mXHpMA z3II{4sP?-k=-;PD{_HvH@2aNC%jYha)i8j_`gbW#|90IW_$N6=dv32Z0{bT{-0qK z2z1Px*K&xcBB}vx-WwA(m3n<#Sz&#=AWcYih1`S!mn!Xg&3EcVrah zT8D-|XtJdX^W6W0{xi|PuK6AG+ zcvns=iuAZ2kiqme72_(Riw1UQoZRVrV&&mqYh#(Q=>*nfgeLiGJARoNV0yd9Mws4? zems6t)EedjfJNgV1ik*4zN)K-jM)MtjQu@OE#8_-i8w~GjWfd=@v=6CrB+vdMjPo| zyZ8j5i4Oh_FUWY~>PJri=N$7&>MN%r;|(NaIRCr_vyE@B2}GcyT2argEq{NF5aW^A zwo)<)N%$&x^}Fves4$tXRlJxwmuUjuiNQk}tFU5%C0y3vW<2=dVdIjJ;fR%<)52sgOEjG{5~gS&k~~a_`zL7s<}dPT zn9e2t!PXROT(QKQQLm3K-et}-VrG;@~2^ZrnS8)%2K=Wt?O6H7%MRbLR z$H(VmU-C4vBOVS=!8|waY{7d>7X_%cpBUohpXalIDegSfRNk5G;0z_0xJlzpDURW7 zE;=5kq(RIj>^=wX6fqCS@S^@Ud=x86KCf?kmRgQ3DUbun4>;z*QXXQ^iOyF^-%kv# zO2Y{4K|kPZ2MO|CrJgLxzMr@um`+-yFez7S^9N8aQyS*FjTbgZ&L+@UY`|L<# z7iNXqxCnIUnQ6tR3O+nqT3X#)0nisE`eG1Yks{=-vBd&4YeiYeL;(1eb@`SAkwx|n z*9fFny~<-ix}m|Bz?9+Yv@H;b5nC8bBoCd8TkTpMR@_8QifC7^-eK{Q{Y!`~p2Ayg~ZY{l5*+zs5%Y z@ewe-a_fl9RSRiZlaVI42cW9szfLN%XnS+()V>+9mKdmTy>F_)^o#t^LJ zk$@;W0rC;mB#IG&vR>Rh`a@tK6hJbZEyYt96|J=cM*~;N;C^cJvey@}Kt?9!)1O~V zFqnstfu8*W9Rj^r&yPhhKTqI5%FA)1pO0O1-tmJDSP@qbj*5o&+giG$lpWpK5D=wA zsj5UsPsf0>QBpCjV4G(`Be=t4Bcb%@xxbzC>h3u`s^%LCZ{Yj>)I|%Is1$o*ec1T( zcDAaFK@mQmV?f1wYc-Toj`IwkpQoJpiH{fQ?KDd~%~NE~`z#Q1sl3ff+d9Irrphk* zyWGefrre^^`B28fkFheuA4bTVH~l^4GYOn1>u*%)hD0tzCy}yPnNKox_wbLmws$|K z9MdMN5Sg)jKt@gh{P3J(0_B(aZqyO>zI+l|q#I36V><%0XjuuUaEUmK>nW2KH#!)P zDvq&!75=GvPk{Hymi(5MMV;$*!Msyu(H1E#qu%iTwc7(esxrFFE6+|T6p0Vw6)4_C?Q8I+iy z?jk_kuSE4C>LdzbF|=NEQ(gM-HS+4c+M9ZD+4eUof1&)pGK zc02u5frQ#dB&AaXnA*zpTqGBR%l>fvvJDS8HvuFO$I^IO%BX0DZuv&4#Aa&<;3eQf zSFD}yhTH5T_292}F25tfDyj)5Z(naBI;hP&KC?zz{y5%YliBjb#UGgM3G4@(g59cK z@Woy+G~%_{KFB$f2)~6S|71>1G0egcevp>lSeJghfTSw@sb3DBL>zmq6L`b`FWH?C z%NX#{lpw@2F&bfs%f=+8<-4$1)-Bt~8SbMoeR3i?375r-q+?f@wJwyzc@|3%*fG2G zEpOAXIwzrFBwvgIM}cj^*%tnikP0K}Xa=Ee6C7Unz9hm4pNfS&y1C$ zLMG3ACbCy+MDolrbX6FsW!x3}w+KM9YJ}GCqxnHEO}sS1?DR5WiaQ@1LGLTd0skye zMlD_JoP)3FGD&4m9fWato}=gJt#S*yd6(B=D>Gr)eR3nl4Ji=yUVaG--%WgaNA6Ju!Xm0N#sA5 zrV1u!iZ!440V<4d)3rnx5;hc(iUU45|A%gTxn#xL%Q9{9Od_i`bEjO_m^PtyVBz^8 z&*h{DY^6y5vRKMg)I)__j={383wd}uPcq`SDJ=iqEuU#ry0V*cAAOAPsEOA5;dnKo z28cEOB|>C*Ui5_Po}T_tG4q3!?>X!qc>Aod9SboW(2d_#$-f9$0hhmjdBuPFu~^c4 z$F;WwP&^y4f+kOI=z}ilS=`>omljY^+)X0w~PHh)?_dI?>gcC**ZbwiScx{x;ESpAm~B0KRiR;bb*YVV%e^8ewp-K z;-DF&izAZbg$RzWuWv^_uvzpjJc`0p&J?7f8SmPjNdXo_8{Ely?^KDTxQ^=59g)U8 zTD&027ws0RuY zivdk5F5u*PAL^5ZEMFCA@R{D_k0{lzaSSiG6(81z zW8v}?n*8+R?qp!dCUZ`KVh-MwZhp)$M2k18dkCZ^2E)PPD%Am30h6APuf7|8oieNNY(S`=e@SiHE zE2EW}-JP(FgpZfC3Q(U=ai9o|?gpUXeO{4p_r+1>ywyQlhZePif)aukw7xAXy>i%H zF1T_1#_q7$y`B)8#|oZnv)kk%HubfAG316p^vojjz)X_S=U?XbmKvWO=WNsbr^F#uOx%8?{Oxu2< zH$VPr@kx}Ak0+SJeY$CR)xlYoJ|Yaz(#-%yE>tk%jmPYVqRrNB3-aVl*0Ck29_&{p z3K79l_pE213cqqiU6!MamM_!Hdo5lP^uxA$#!usHc!bD!Na~z0d84* z(mk->CBvF8YwwZWfeJ9qi@E11y_ZNh`Z{&-hQq#`Kx93izkfm+&`TCvjBL{>7CFYQ z+lZ&~ujubWWL|hxKR-R1{~5A2QJRw4p!f=oKAv6}=N&w3eSHsu zXxn%8VZXi2=#!wDcsAFB%29nam?34ITa;Lt6xT+PCpL-fwZRo_ig=a-PW<76D%_O* z>?s0vCQr&BRnq`Kt7=r}0fY*NLbu-KRqR+{@ClqULz)_L*R?>$wUq|ScVDrAM>^dYb(aNoV&u>!o6RtzH#@JuUm%#&GW>t zU!Wc{Eb}0lzT4!1r?g3=hQ}D*TY%}P%l<0O)YB=3GejNi&xH+{pM3!k^lPhE?`x)ERECJi=RDow9;VfW2c=3dI5);YzxRwiTp1S@*EUG) z?Y0OF`Bxud&m08C8V=v{U-hw=ahjH{Ir*D~2L^4NQhyUHFXVQm_$2Kd19t*UTy!X8 z6T251%h&rCNOQgseN6e_8EM*PlTso@Qn$d>T2GKgw-KOEh`SDbiPWk(AnTMp2Q7)j z|HS)*8DXxhEcPsq&=7YvsbA*m4q4L3q>q8}*(+)M29ysZcXKb*AA6OWA1CLSaOHW1 zsG44VLVLXyO*llV?jNv_cCQ8t@HoR7Y8-Md2G@5nbkoMGmNU4+QT{*(=O;?hp3CB8sUdw^p_!R%#n74LeIRjFdD6z=?x##&XFJwd9MjRZVCx=5K2_Dv?<09{rJkn~WPffiZ%)CIS`X>r&! z0P1E1Y~*V8HYHPgb!MM7)ipMu(>CSw)2oo>F0Y4|3T&Izb-XY2)zLkB8d0LeKCeIG zT-Vg=l7iSk0Y)e%D0}VlEZ~%iV_Vy^$04gmwqSiWE&`01k5GeSWN77pe}tVW>0I&)Fpjhm zF$2ag5MKP;!R<+Hp)C=9`cw z;ymQ*v_{JnH}4bpAUlNjuShzsd)GEdq8lFE5{GiIUk(LN@g1Enxw+HxlxZrR9s-7V zh(K~5t&S(~WU7hk~ z#SGpz>{wjyOsG%P?A_%Mdv!IZU{Ct>QzkTd^=C{ofGFl?q-7k2{`t)~L^6jg#%_bz ziJ|qpm;yIGvla(twb6+M3tqg?pA`7+r5jKB3~vQJoCM?+wr?hE)c*;#1fimt7dO~M zt)dO>8=`DqO98XOEf?nizKWClw$^CoA>sq@x1QxW1D{?vE5=z;-T+p}(1~6$`7mej zSNZCvMRqhCUsb|{*o$I+?#IlZB}CGn<>k#Edq@&6y5%%RdF+} z=38uwn34%XYz*iw?o*N$=g~wi>5%XijrNBV3p!r|#v6JGKGRoF2kA4auyqFq7rb%C zPD|Pj>sPsZcgsI(RkE1EjE*EH+Y7@&$H_gIIH$q@}*Ff zO`)itQcn(qM01R)FPqwXRz?@N$Q1RWyOzM|;(2q|DfG)r4_W$DmMt6+UG(54kb?M< ziHw`a_UR972|K7Clc$~6t{({o&mn+V)b9k(KMF4UDwxe#)z4H{IqfAJHGC+(P)8p1 zi1pYv?S?jVKax&+Jq7&!WH>+FJh3fl!>`Z5fztwpK}6Q>{1{wI4zmQ`sbT|OzMYa# zkH}SjU`pjZ*I7niyZ_b2fPadit#{8ev~TKAja_;5o~~`D?%lc$WQRv)==FQ?SL?0O z>P5vrDjnHNUe745nzP?6=TK^o4&3Vzm z=ocW5`g^0Bt;}P!y>k3-J(vI5s`W?ZO60B1W2s*t#+iQVQ=^c527%kV(qACRTkwOs z#si|kHu0bA)kOOG_mT<@4+FPkUILF>L6{)xslW7N!ev&kHfI*D9-z27&G=9o!RR+i zBPH4HH8hBQ!^DI9UUPUwuZP2t2^)SU0jUd<_6Hr;v^f---bAo9eBAarHOu}5g7?IZ z2dBW!HIPi~>`FU2J$XE*kx3CgtxBFSBg_X9Z%Gl_robjP1nbI2DokMk>cm`rPYFX? zMj{$9T(LwK$^)6s+yK6*Uo|X6+s+BFV@85lYa%~8G)NYVj^d%g8UQNUGlrO{;( zdthgiB6+~d$U(gwf9!ASCXw_PjjuVCP(Nu7%@o) z*1uy%{0=LneiCR4MD`MTjcRyMIX?_F9@g3vG92txO|cpAS7()7t0Wy{me=n+0#T9B zT9{q2HBt5i1gnb!rJUrAbIBB)nrx3r=!`#Xt&mVyG7k+6IBexd&Uf&2z%sY?IU;k; zMHii4DyblD)a^vx6$1#UjyC$OIP?Mik$^&FCf@;wR#MYvREn@elhc8hMt8ldhyZwi zXs2o~X4Tq2y+Y3&Tk>*Ss$nfm;1tv0Yx`qqfD8B6uX#EYctn~eScCy^-wZxJPGVI; z!3MVKb1DwuffH(ta2IR+2_(LkQZ^D?FQHe}lG&A}i!YR~-Hv$`$49=rqbH^N+HSKE zH9(E=19h_+9YXvy#kh-;q2YWh&JrbiIGqXhuKRW&Cr~w=q|>JJ$x7Qkg+|Hu^wlnw zA=B`Vf%$uzjcWAnxbg$ycS}{xD08zXbnNRa;wjP;`Q0wjGMijcMr=Fh0@VUqE=~=adH?_16nNGVcbN4ozTG-!@F89~7 z7Tk{*r5*2r^$zk$_{Yfmg7KY*&GL+e$;?&jTHeD9gSL*;K!IaGmtVIj^L(I4iNn{7 zewD^#_w7EX**y$-J~J2FbR9!medvm*V8!zSJmE#-u!+ms7@&bIq>G9YrtzdyD~L~J zR$R)ah5=0GwK=g{MehMBaqC|9h;M{DLch}pBx`SJo^kX_&9WQD>Ci1o7d}Zl^sjx- zpGz+vg0oUs@-dqhsKH}c#h~h%Kmq6YfYh@ z(T?Ieg^LGS6rFb9v}_NM0V_%TxcGV?Eh8sv#PZrHg$T z1@^yl=>aGoB?bLuu)xx9**-i@sG+XUkAL&Z!}V<)kR*WW)y)drq@zTMS5lQy_h9>< zD@xgiM5GK1NZ4`o?6K2lfbZekM+_%_j`@!a&21_5H5R12SfdqVI63I(SgJQ>lXxS(VI$#tNub2X;VFBb?>)(Q+k!hu0cFS~ zeq6n4M1d639mMuolC20sW#rMlD!j5YH=o1|x7=0-eU>IQ z6~45FFT&Jm^E5sLE0}uuA)()BT_v1vhh(L0h*D4EIa{}s&ETaOdZ6eTaZL4Y`b*m5 zsiQZe6EsWo$b{Nd3Ry8l?s$E-3=m7_jRtNO#0m0@{K9 zM2r`CnWN*Tja+>TPwNv_5}aqq1whhBY@?I-N3g)OdeO%F4F*$OsQ5c7>PM2V(LLqJ^hQaO-@Ux7 ziRh+=Ym->lsdDIGN%LZ^q3L#rCUXC%#1MDb-46=T2x|-4Fr%=OT71OJb&ez$B8xQP z16E-;l$DK(iPVCEH;gy%S3|X-AvST;N7Q8E8UY)bOiox%${V>aqvPC5&11QSi6;%# z>`2bz&y{6WBNpgF#Vh+eN$%m+)f5dKu320p4MkYD!#htR4qtp_I!1+d_8ZOmKTW7c z{p5wnltFWX>_ydd?h+TMv0)o5Y{nH+?d&46qO`35k1D+APHHUmHsHFqHaZ_!T%r0M z{|fETICTb|ij}2)1a55*jh6^hgf!LMRtTl9L6?5H-)7u1&qIw532E7*=&`ZPOtCSW z;>dS0fvji8_g|&nHt8mtk7D7;4Vl=SGGA6{klmxahjildhmbfdBsX=Wh-7l4yDSfV zflsp?PV9|OyI448D}ec9lm9R>3oX3Eey30JE`E|-i=T}T75!Z(WBU(;lQkUQ3~6Z4 z+y2Kd{v5Am*)3_3L8RkP{L#)i5Uw&{&o<-SInb3C(ob}k6FzL4Wy%MX5o&B^bf>{S zmcjg;tM{pw+;=n=ztiMpz$QF>R{Ln{c;>vfE+hUT3EWy^>xVGrC@uajI03AbW+09;y z8Tx6jZwFKh^_!u3QNMdco%GB#x^?h)#V^ErgWqU1rGsP~(MofU=|_A_2{gDweI@Rp zhRL!!EmB(B!W#|24>Z&%;nXF~K4AhHWm+7aiGn1!I8G$L6KwPHklYwr>TQCkJsf;$ znPCz{rB>@4l52*DgGmGUHLkdBISUnR{7IFA+|g9fKi+=3R0 zOK}L&7A;yR6mNmzP>MrvD{cjfLxDoE6nA%bZ*eJ}ph5r7@0>Za*32_=&bjYd>v`7w zVt4~2kOd@Mzw7$$y+0fOpV{wZbw80ePby(%+X^P*743{00o>W^FrlsfPN^xC_ccbp zG^+hEm@^n{^an}MW;S=u;qg%RgY?~4D2FnlU%5f#()laaX8yBJWkR`w3fdo6LV#-e zV{!ZKYwb1h8|`X*(`=hd_Ewk@cWrx+KiQqkx&!pmK~Z^TEnCkn&0s@oJE(LV*^b`CexqRK`GQK3ZmB=HUgl1kq>jQ`^0x#3=KcVlW~4O z!hSxFgh!t1UpNJsQk?Ul3m>fPF)$^*v%VsIc7}7b$$l065pD1s4y!}@f3`qwEeRQx z>{Twb4kqNkJL7Wwo#_g+OUJn)ItHD+JhojjN#1NUg*`~m*|XyJy@aVTgu`J@sQik> z1DfcRY??^}idvD^&jf!af4m(ZDN5dq{Dpd3j>_p+5uJh3+nq*z-MxKvzoKlURB#)0zj(ffCVy$ccJhu9hShclc|HQ%*T$VV-Sa>*tMiLD*^}q|I>3q;)9xy3@H*mx4mir} zpEgm=Nf&#kkBAz$zNWQmrkxLJfHT(2Juun%6J6j~L{hcLcWs+783R+Im(7_MeRex> zWOuwsr(h_BErhH^u}l>JUFz0o4L@`KEwd0(u}*7CN8jKGT!JBaL{kcBpJ7=ZR<$3> zcd{)7Xgj9b@dO5hPn|xuae4;J14TQw@Y)Zvyhn%FYs>Y&n9$lT-FI7ER#;KS@H-iV z)L$U7ce7RvbKLLkq8e7VSC+RWHD7!M=j}cTfNsXHu{;*w^oBUjM12;la%m1UQUt=t zkJL7Du=i-n=dtbdO=Mf>4+9$prCf~b`DlbCSlb=vdN`s%Xty*MMk#q%Zc;&W?2j4PzOd zV4ogStN6*vPnaEXk4C^$_ojng@0G++e7d9z^9%i%v{f^4qiinG%hYOQSUz%dy~~sQ zXuZvFIMxxLGlh%d_p%pDxi|M;AS@`W=kX;Ls@21K$(5jtv`dlNb8L%M>|N$XE@4Kw zk73d#*V28Hs%JsUj1&eYmWBGm#UCg$YIlu~_yGT#t*Gr(Z!3foAo~afPx{)8)LIF^ zyqgn21<%>?%oNno*;UqE{Ug+nTs{5`e#z&LtK$<<{F%FfbX_(!YqH=dwdi#wUHHeHm5 zPq8Ts9Vwl&grV$WSKM=iLI1hz>n|Lcrwpy7e;~mt^e6`#1ee@TBxcH~cx~JfB`H<9 zahJT@8I=_~5C%V98jrmbUiYOTv@@JVm-|%8`u5G1#IaV#IeDL_)n;l?i|P-a#FY|g z!BFK>y{%XxK`dDLt=iJ69DA6(l#EQ88DH6oXhIZ|*hliuYZU_hg>DS&2+HUdxlIis zTRlCRmpKb;?zr!Pqg&kt23(y3$1-C zScOE5tnx7J!%(r?awnCz@I^`%B-PTqXv~G#u)pyOGTFonj)!VTepv2~0D}DA1BDxr zgx4Z_J7&3wFNd5u31eS^ynqu3gu-)Uxc8eF;F)*d6iYX zYe;#!{eJmTl?WZWcLGqBC>&b8TkAgHIpKHo&}t(rEkFNiTJ89o{BmDD^&fSyzm4!= zqB3Dpl!HJBC{FjIb_K6F5uOuQL7A zxxoV=>CHDNd*EbuA0}(<#CK{D+r9S_)Kz z)2|Q;D8IFVdhMDi@m@M_9_AZ^bivBHrJjag9*-0h)cjIVs@ldb-RZ`Y3PIvW-xBHx zV-j$#zS0_(8Cy?vWqcf*^NIi zdG0KCLdhCuf$Ml&Xhb$^yye#)bXWuI{-fruxYhCQ$W z`!mcm@+oztDiiY@-rxS&AKxOi*FA@}<)yz5L7{eDZ#q}_ z++OXnBq8>$Rx`Gk{hDB6Hyy`d=QFf%Kt9`W3VNUdxLivNJZ*+LNjAEc=H2|J>)^(U z45is;F?JeJ+EZJQ_k@i~V61ShapCJiq)S&Sq{iWUdAY6L<0b2K^6_}79|U}if{rn% zC)jiZ>9>lP{w;>9;S|#<>hH6)lCC~B&;hF+=)%EHf9jlzD~@DekC|4|o`RJQwrjmN z{(8@-DB$4OU{rtTzdkD2xl~uZZp>g)q-3z8m}qrIg!og20g{%`1h1UydKP}P zx|QvC<$&?y0v;iJvJ~7Cg>=*iHKCt=Rqyv=x**PRl!4j9h=Oo_dk@&I#YUo7lu;>HTGz8Sq#J6q4cj@qSqmo2f=)Q)rKk8FkaESsX;y{L+O)_(iE zo2`tygp{0p=YwM-;6}RTPu6_fM5HF_{6qlM@%3@SktxreJZiQ4`KD@ISPt<r1tNEi#^yc+kq2F87W{(W{VY~Ih`LamVPIaUaM9AsOJSGv^fvMS@ zDr(#faXhb3+4v&o?VQxC(uUfal%=QTFPs5e&<$@SD${SmL>j?1LKD{;<)brs5=jN^ zeT*A4=1g&a6yS1I>TwxxpMTs&Yzu`R~J6dm<0F#8gFNd833_+z(B4w82iz8#!mG>njf~ASLzdV;8 zo}sb*hqJeuEfZ++Nz}aItQQPl3f{MvZidGQ^WmZ1NUD+bHHDS$7@p`|f)g@CyJ3Qcl!a1Q2EQt2*T~vO-0j0)9(xn%H9;N!6 zWCay&B?Jr-SRAL*`++*!=&P3Lc6hR>bUJlr(kfx|K^wFZV)3qu$OmXHQSbI}@^~7% za^^$4-fKjv4itUG9iL_1=yfpgA=p2+QJ@(3q6`#@T%_;t;(W=jh*`0=<6pGtz?{R5 zz2_&>b9#tLj65af=3(OK#j;#4B}d02kDYjT2I6<=QyNI+n$hv~`4%SCp%d^4;r3jp3OK>;``-YlHSfBvH;r ztB__x+q5Grq)|03-Q!km*uf!q8x2f#o=e=cGdkjIq`qoJLch}3t4!Y=uW>Ulj`Zgn zYg4$-J8t7N^)xhYMU2e-)Za@!={9F zhoLUvm@lC$nZel?X8?3-Q6?@QXoSuJew7H$P7l(6L`kgs>70?8;6{IXG{YzMbXcE6DG1$((e){o6B!vce<4 zuanvM-N8`Xwbx$VAMRgM#^<(c*zl||7Deq{3DrTOxBD$cZn_dF<>LcFwF|kS*AHghhfT-U!3hAmJ^_AZL z{VTc8olIH|((}@LrVcrxabh92@U){KS&5VJB};S)M1%vq<#NNvu((L3If?2Lj#=N8wCwiciPZAZoBp?y-fUkRuDWyqNUC0dt_S3T*h7D9dT_b|5Aj6NV3i{srg; zvMtNtgShI{_Cz1@WjNkiFT3a_NNkx3Q<0KUK0UvujN;<2@cVS7iige1z3Zt0}p{(10_7E$*c#LQ0v&g~S{ z^~@VCy%o8kPnnf#J1wWn>#3nfQuL_`>NeWV$ti%~O{K$vlPinhu{!gAzh z4J2ZPf6kB1)`YwbF;XX?+a#-}DHx{Q(s{;V98Bw1dydZgCt5ooP@Rk&!@6v4ny2@$ zp5!@AM8F#+Su=1^D+rjoDPH=RhIprkk@TH70zNDMtI|;|1X0m9eHdv&^Gu%M0eVtY zGPkA|6BL#(CSY>1VTxm8!aij&Q7e4=vY=PhEAVaDbV78H07~mSioLtZBmgFiDS2pA zckZT5CDw^)jtP0C2@bvEoq^)~Mu|M^c26uX+f)^Nu%5H}h^;=MpE;l4H&{o*mJ{Q4 zfgoDAlQ=VJ{(8E1na_N3oxv{FPtUb@0blg*lqkO)w=A(W5H5>WHPly$msd2?e8MlO zSLOM<ATV)j&7eWmu;) z(o|se4h?xUR{x~b28p{7slc-xMJ7ctCe)08mu#>_+`h6ol-<230QGJ42N9Qp-doST z{$pTV9(ovd{JO!B<=sJcFtoM-5DHqxPHIGysP+<+ia}BSW0Q$MzQ>Z`?bnh1Va5Qo zLjYU4Zn}7%aXWTa2!9<0-6wH) zsgyYq+y|v|z~-TSk@`05+Cr3xjy<`aR_{nn;- z|CM#I3^icTx%-uLAr#E;!Nd8;)Irq&vu<*|N(HRvxAL~k*J z9)a%warur{d;x4_u{^;_h@3u|2q@E(pNM_Cotpfmq#bpX!kZO}C(Ja=1);1LcQz|e zY1q5tVoazItDRYLJV(N6@;Gi>RM>YfLpZ4mxcwYPJbQiJHi;L3Lw`2cOjtG9l>yHg zFN1@0BKW;Bw<;!U$~Z7u=rl#YBVTJ?6xJc#PY>i6boORRx3pjlf|!{p!hZs#eH1V? zq(^v*TY=|u)d`En>0^7?Ud5a&g}83ywWWJzCc&<->Sk&`1oMt=s_cBZw87zG^z9NM zbg_Vo#i%Mwvym|IlbG;1W}W-gh8GWG;!xgfFv7RBX;ipxWA>#Pg(P6f|7AwutX+5{k=8D=E>((+;%HqmU0K!Mnmem zx|S7ZYX&-NT8X)mSJw4It+)onwrFm^m(Fu{fQ*dxtnG<{jd@xK?*i?Hj&UpqBY5s7 z)BTq_BFkvg1Mk+00%M*Ec4fwQe+FKI>Fxex@hMhL)C|;&K4A+Jt1AX!UF)FTi5+ra zDAOp&PTglB&haMYZFY)6)GRvi0`pdUkp={h*RDhG7Yd_f@v3-g6ZP5s;t`kBO4K7bj>(vtrORr;*n$ZT(r%nZz=zF_Z zB=O=1cYmX2ktpz^+LMFL_|c~VNqZQaK(BjV`9+_TUt^I3*bQfCMCE>KIF)5h@D z2&RonD7}+wHpldSIXaeO$17SYYv>)XutIdamg8M`D}O*@+r~E8Mr*5fpK5mH+&6FU z!PE7NGfXYh+*&%ltqgG$TGNha^W)vO7sZscRV5hlpkK?f!_IS6FiXU0H&mj^P|a4b zO0l8enMzJVh`>?L?4w@gku2m9YIXf8xDD4gxYq=?LHU9spVmxvXr6Yg@8jD{0*q<$ zOK>dUibB1tP-47&HU53fg#Y8m_tFO#-8t3cAyP7>%LkM+DR~vb#gFHw`tQ)wFelgVD~AGJ|ip=la|n%VxUIw#;ijK`qzDV3hXRoLvtKY zDZeX${JykNBc24W-P+_$b${MTtDr~Z4cBYMg8|7vRpdtz{#V!As8CU}gr66KrO|ag zzF8lS{p`KpaEZRvk7oN zU^?FlX8I2Hx2-{VD=860gGxV_WkK_qCR!rxcxCsh8+7 z6D1yWH<$bmx7W+L46EnuTb`GDr3m&5$~x)|xKLwq70V@yZRr)Rcs^E}X;HM#HW5VetV8Gl%b2aLyqc(XhiR?; zVdVQr{(LX(Uv4+EaUd8YVl|+`vN^~nMq&Fim$UmY_Z_;RskOLxE-@>r5;_083Mm15 zGQqnU)vGtbJ*5CoGp~*Rx}+Vd#7Vo-%C*iO?5crjLtNNd`n|fWDYUr(ce`en_4{j9 z0*qL$w7I`PA2iTCgL_>(H!gQhc^uZQDK?%&D>3slaC{047~IR$vsr_Lcsm8At2Wd+ z$lvTZG`CIjC&(FXLO1~SbpL})rvrPms~1)9$M_yhU*ryho#Ccm@Y<;P>j_~(J%cFO zx%_6m%H)}W(T|UD$UmX61P{RNki-G!IV84oH%EW?JnVzmz&7%^yX=ga56@O<@5B(} zapfti1`08GY~NBI1u11lJ1PKM)x(z{7zVPrbKZNWsL<1{C!2%e$qbxlBmEZ@1%n5k)8pebsW^uwHLFTQukM=aVNQ(WY^B7pg_rI)d4{N)I zGUfFW@G2@v{@8O#-r>oW&rwH9owCtGP^A#$&NW9PsmPniD+zL=#}^wXZ|MTFeXec?&;~UMVQ3ibuRRUr{*ZFe8#ZNxxun zqz+HJlL~ua_QgFf)%LsK?+HGd3ILds5DY1yT)V$Oh%dIGju!&B^~x$$rH>538niXm=V)SVg+Iu#4Ey+aJD4)~&zDT*UMmCA~Svm(n zwTV^!7^{LxBJ&$D2(PE#@Nvhti?NiRz65!CM7DuYeEkd<;T<%sC3RWf`^j62C6RPm zk8}w5*^6BCMOLe`V?4H&QB7JU%ZADPr7Q|0siER}gB|cfYLC!Y$=Xnd7P@JNbwfAH zGX|sJm|dxK(C)b}SbgG%+qL-x#hiidm(QP`^VNB69%y-%cHK}$yZ&&6Jmy?kR>J32 zOYR1M;y=LA>3bsYoC9u~j<<*ll_eB8G|A8f#{GaUSm@hn{}1KNWN|7E`h+YRtljUn zGUXvDv{%QHaZ>Y>O}t6p_6&?9M6&9H131?#cFiyvg-f?yw!Fe6o&jD;7Z-&wWQ|^0 zue0$8=rl1lZP*g}L^uQdYRXqqUN<|Ar-Cz`)1Qxm@R!=GQOjj_&nAU=WF9E6qV*Cq zcF4UftwYSrWjrd>x%QlUouMKq-}Si91{O`G5j! zWfR9pFo`Pw5Zt)aC!1|EPA3-yfq}704@$>8(>7RdwUWY&%t)qM^LrSGfX!sYos4LR zTe-;6fnvjR+HO{*gP-wAOC4~nJy`}4=MorMGfEWesElH7eedX-oqoueR&xHx3#iL9 zRfWvui^j$&Kax85kM@N-QR1a+Dk@k4dD>MS09$pLPd|3>w~WJBGAT&dZ_3wyAwVDX zD#zj_#O)1FhOAZxeW(xj3dV$Cw~ObSDVd*pR{||+x(`TQ3{Z9vkc7YI>scad z&ete4G-6E=NM7pL{`f1_ZWHJu?-%WN0{X$5wdGxXnOG8+QAtM>RcPJ3)7g?+db3NS z-M+Mdk35_9XSRF2Zvds`?~pYB8v^LM!7wZ!$AWHSt}?;04EcJRYL++Xl8CF>@*zMf z|1ujX8qvazfa}fO@v$r$f1PaU#Z!R@gB37cpR|~E%HXtH#gTZT-6?KZm)A;bzp$}r zv`WT%W&TSJ2oBR`CtqGxezVDDs!CFubC}whdlw!vBN0#J&CgPN4$pS-x6HvdF&64B z-=7m1S&nI~*`s6K{oe7llMXnV4M4H>y)PThH~m42rnDzS`f`rxA9al*c7&c~Xsn1s z3+9Dy>HqkF5ZLg|Qg;3Pnd6J$K~wI|pF<^gq>5Um9w^C<6?%k7N;)*yj^+jS{MT<0 z1=OrgmKb8}k!Lw{e&g8$$kx9=m!BJ&5$CHtH>zyaDjpO(OgO4SR!J)X&^Hv>MB$g; zQx4YsQ-qZnI&AwWtlrs^yQdPygQv-l+H1f^7`ZT+wmJlFlo#0w?;J^CZJ;S>atxA5 zms73-EPU{Qfs$J%4zC*aDU`cxm3PBs+n&n*)%0U1ES|%>K6hNs2oeoM?f^GWn*`DT z)&I7kg(jVvz=h>y8wnFz$`k8h&dhV{08SVKA~m!Ha<|vU9fMs@R_&b=2K7%m_{J zV8N!Q1=Hy7?@C-MYiK!&%8gRNj<>qSU}JFH*I>$MbQ!d zF0T2K@d2`!*)gNOf$L7;H{eJiJ z(eX(cADmowr_M7!p^gac9fP{0#RNF-j(*~fa?MTz(I4)H13%V}t5FL0VZ&lXs*}>^ zd|(`#wkbojFA>rPscDHdT+kUn2A|op({n{3h&v&~chI)PTaDQZu08hm$BsL1iIn=O zvVkVJjl|y4 zc!{Pic+h7_=taRig1ZdDdw&x-5r;_Z-zTwArQY&?*$r|Ax;Gk-tDVG{s1vRFxuS@g zQ3cYiUvIgcRSr>uR!jPsKC^siFc3m9;3P-)?Q!k$&M(ZRwAhUoZy1l>EZ~4*u|0E% z^fm!imo^R;`Lqh@G$lruJjA1h}G(ozy~hdl=S+D7ctK74=)eD37UOCQ;u#D(3U-l z+~M}OgyZX*K6fLIsQn!ufaz~*oax>-_62PPri&_HUk&x6$uOOD%#YGcrh87?HkUT} zsh9|7Okk<)1IqNUm*Lmd~Js!Y)oM`kySh#QMnj z9zJ{&W!E&SYX64IN7efGH|~YEE6@10F@l4f=ds%~5Zn*6sLl;CRO2SSfuygfM~>Bp z%}?J{3RNP=en@R5asemsq*o$kS_oi#YV& zq8J&~xr-<}^*A6<}u0vV9+wO9gC2+XI zope#RrIUxVOkyBf^3~%2Fe}=gVQE9m_|>=(05AGyXq4CPEE3h3SmOQV5*bgBk85P9 zWv`$6{sPPy?zhiED@;jn#>S22`)D$eo_d#Qvu_qlK$w`+h#p5ufED3?zR6pTRF-g-?a+O_tuJHk(H z_+H1th704z6?|zQl69gXIo2%FNJFRu%5?qYvjCUv@*$jsv3&IV!tPKs$BIrS(YFBT zCseXW5vuz#2=OH6dxA4>0^5cvbLRES4Y7f%UY~u>xihW#6DhK#cQ0t{%Xme1SqU@T zH`{z|`vakmJ)JtLe7|F-YPG;98e5)nuLUSPKLWE$&F2?p73lG}(U(D;wc9>)wgfLr zI|GMxCK!08<{rPVjSJECgLzrFq0UHPM{PNS>L{*U7m>B^xqKPmYo*o>)|gytu1*w2 z3p@8+lL6XnHdX6p*_&G?;g;fwYT{GaejsXxf=skNy8?uNw?7ezMKwcX za`QKLPikZQ)2uBC+0Rn^wDF6%*s!_qPLgS^i9))@BHeKby zF#XA_*T+GfqK~9Uz5RpysP;^-BXz20;I(|>!GbG8Y(|^(B9S1gU@8Cmymg;Z7lZm+ ztlIkQx4%yX7+e6)b=nIXp51?=Q@J;6?W+MmB%ckJYowoXU z7+2xJ65D*ZF|^mph3@mqpZa4K+T{ze56No@enTb%!5S14Kwz@fF z8QPfNl=sVm6%b+{^-qEj50?qVD6T^46BA&eC25MRtJSrP2 z6omAdRrv;RxYi91b#FFg@>?EI!G?*bK`pQML5+!~j6#e< zsmeSh;&XJ+h8K}X%7<(3Y%uvr<#qAmd%Su~Z(`e+^2=&6b4K&5DmpkI@hPt51o?px z&|u$ndVDpl{CzD|f=_7CH9;h+J!aUr%OGR$@*5Ya8Ai`|JW{hsipXLy9eR8 z!HqkaK3Px5;f1uwZlg z?Ta4yroW8)2Vg5Ynnu<1)j>15yvCRXMp^2py zKiDfAbv?)mIoYE@JA!P(G?Uj25k$Ja@;7+d9EmFeWTt|<>>jNk-43Nu1k~cU`?(at z4x)Xy;xx#Ao%ImcE}&|Cr?my>IC^SDoPw!QE=#CHw3T7=0zI#{zvE=&c2h z6g`BLowlj*PL+4j%T&rL1&xX_sO@7H&I?^dnP}FIEFX61ZFOZj*+=rfdr1M|;b+LW z?l=NZ_?v0IT3R>z*5q%y^cdo^4 zeN6d}yZu)DpgqR)r~3Y$F>=Ql9;eSKs6< z8L>e2bUSS`NaC=X6Y{;+do(`gsgY#yNBV?tHgYFx~w@w#~1iVZ}fAHOsu1WY2wv?ml4VJy8H#IvWjhEGDe*l zPqQMTFKM@%5|v-&)$LdiCj4}T&pa)En8o}d$SL*pI^OKTtq-`K?gXy=aOAC_8eih0 zU($=`Jyeo2!z-3nq|PN8JT713OicnO`_EQxvbSNubP+pB-|q9B=-1C286>Bwt!Oo? zVGV!$YA>q{i@B?q2Y^cE5^lkMX%9W>?{^YFk5t9rhMlF;5w7Z1d;|LPL^S+N7r^8F zWTmS2R@0b!DxyNI_p@}wHA}A{s?qNN+1=d)t6NO8^XZDzU?sY^5MJ%}Cp1ANwCHhL zx=~Y87ib1-gYd(an*+e!QJN@++zM-|0@PZ+kSYt34}7Y<%KJ&0P!3Qb+b9tPe}U}k8k=Sv zmy?`fgU^OODh}PsQG;^~K-blM=`|+w_XQ5M#H8vt8LB}mtp2IgnaPZ?trB|RTa`WU z@jE_K+yl*(Eau7MLhBP1uqOHoe6t&xTRULV4VGd;QXDS<%uJi|<8o>|HK$689%+C41o*#D{)sG4Jz zR>1A8?ZM?K>HU25fgg__7>vJZ6M0^+fVx`W&Z;r%v@4UJe1+Rhnv0Mw{bNe_FqTa) zaT0oQfB#Tt&Z@|e$84k!b;p@ogyw@b)tvL_PULSBj#^_Gy%{qt-Xy_ah9#U1XJlrM zSQ-!&Q0b-31`z>nu>_;cHUN(3i%FnDPy@3WZHr)(dak_CdA;w-nC*k zoa1S|S?xJd0{b@>6i&g3G(2CKnna6piI&&Gob1uNm^Htec7SB?TIrrV(qi+_ z_OQ%gXRn|7m-OcUsT3+tQcE5wOXOI4Mtmgv-AFa>j|2j%+|@9(sbMpa=INNmyAxCO zr1YWLb7N7HXNDxky+Si40OCVrNraQk9Q&bP?jilCVILas#X=S51C4V8@< z78MbJ)UQCT;kK7CR^1YOpIy^jecwDnsKhX!|5$Et_|+T;+H3{E+L4?662NO2jhD5u zQFXk&fDo&s4nMTv)p}*N#Jp+l!A&XnA1H0~*mTFoh|MYu1Cp|ETu(jyhh-t7)sehp zitR)lUB>{ze2{xC_)fFhS8~lXif2h<1)$UXicSbl3^}hbUt6gqia-Rb%rbs9`Xy)e zp#UQ|B}VShZbkSv&2Wza{Z4ps@Yc!;q?pmC3V!b(8I`e#7Op~+m15k>X+~w9NfP&= z62$?2F%YohU_pFug+$aO&u#@D&I>jxKplF_^ZsDLdJ)-c#IX-Db!jbK^~f;C%xj*d zww_3Q!xtp_%l)Jn@#vw{cU;kvFs!^kCag0s4J!u6iwMkmwxG>VKeXyW4B?GHfeSD( zigG-LpRb~MT3-D&R>l9tY4)=&>6198;;YTf`3VMmL?Jv#N*LhIsEbuiKj)QYd~|5o z$ok3GHV6hqoTAzQR9KAk^0IFRX{d$n?{=;}IdGBia{P<;ztzp91Y~^3K*QFnR|y(J zLKLP&ww|PD5k!WqbZOWc1EqnnEw11DckCIQR!#sMY9t4`zW*G2Q*S>+=0Fcwdj3Dc zInn-_{EwIulb8c2BBl34iNp^8d|;$>vD7C|AQ>ZZ`adFSB|cdn0XWmb&hJ})qdijr zfJz0IsaVu8fFBJUSCIwQ+`m1{Q$b=CkO9Xi8Awaf0Jtj>0CN64henT7JO>^WEd93! zm2mqTAu(Tk4Y-q<%%k`3A`DRYoz?p1KN86q1(9i6xQwe9fH?A5sPo|}|KPF%wzAot>b z`Ro6)(ab-)T?cQnpXJ%d$4oLl(zf1G6PZ}l7rh|muw)mGUU`uq`6-C_mObKPHH|Da=N*XJ4zfCTUF{ z@rhvCeeKR<*wR6K>&sBJtn%js{rA!pv>BYY1=@e));`*1&BSc=CH!KmTKjmH-bgCo zVYapA+dY-LiZ$|``e>qR*HOV6fb2K;gfhHx;Z`3Gb*H0{7SLTA(fZk-(9rP8E4l?6 z+Fc7HDTcj39c-M$(TsPG(kjMs5sIouo{Ezm*!(iJ`ppwKS0km2e7*_pSbPT@R*P_@ zgrEiw7smuuh7L@x-!rk*3xPa=u!G+O!}O>tYxwf{%{}L*>6)if+NC8HTLi)9aLC++ z(%rkY141X+S+?rhmea3NitpH_-EpK&aVnO(;S_=ETykYA)nzKn1?CO)BN}44MBkZc z)G_Il_Vv%QeUNHu6Ft*aDOsHa;M~35Pc)`6QsNu<;WgFGr$sTJ337vUeaFkaWO`TQ z0QHXVv8TUctL5(!6Sn3p@vfg^ekL_&=i+DD#1b<{d_ntO;xa>trPz~ zlIAMyg?|g4z0`Q*n{S!iRhCiN2PvYr6PfN`A#H4xctII3%!p=a1LpoHo=Y@Z;GUuu z@Z)v2d$Hn|R2i#4r4PmkK`a!@`Lhsr2?3=nn{%y{DdX|M8aRzf%TE`7TvWi!JDCCHnVrs!ML+X+5%TJ}q z$K9mHzUivTkJtw@=^W{ZI4~MDSU*Vmf5{yf9A#IPMOQVvvaVhtB$bw_ z9$V3|SJ7ym#{miH()0K*$CW&$G&U_uA8|i3R3S>yF_25jo^WEsso*E?NhR%9`Qo4<`JTk8oM73cV9+WI4+0L?*G%vEWH!QiRw|4Jrap~@hhQE;5Re#D3 zsDGDUvh+RmKH*7`)Vk{xT0*nKn1rug)=wln{6aY_lUI(@gAbhf5ewvw|4dV!E%WI0 z)uT*^xBdv`btQf5?}$Oh`QcT%JHrh=BG0SNH+A-dmi}&>19<~jx_ZU*cEtUD;z+fk zUKwJWbe9@;51RKw`&Hyv^$bemg&^OMACAR%d4!M=Y_CxPi&)XKZEaQ=S1Wq&IrSg# zy>xD3ui{rM9!8uwnp`sKNQTr6>mdCx!Y(tA7__Jye~m? zb=DXM7}ZA*Yk?$}`ggl1U+33v)RU#RRv^<(meI67%t9K+WdzZ^ZsvfZkAG98Lr(UM zFGaaQf585HPkZLE*RJhzyfLcvZU%HI%O5OFq!TZmB$-_P^8#*q8Sb$SpZl43=k9g`%EaN&$ zqu7!+@WjWSwYU1P=Ce|a7m=Y|0NYOeNT^FrX6)C8FZah0%(9zaGJJlV zUuZ%^QxHarAoC_kWjZsxEMVbG%Y#YQ3K$LU*lMrmMVHbKQDKXle9a}+cEg$<`k1H0 z?b|#>ZDttAOut;^DSS2`_#ln)@9`e?w0ey`wPV5X7U%jyLQwG_)rm1>kALJ6W%nZY z`Rj<2}#`d@dI^n#dLOkwu(2;L){!v11I;A%jaVV+=BeqUG;5#?44e50pH`CC$HHnG~{H7uv}_&PKu5|+X{i<2(N`xbr%Mt3ULy2}hFo08ksv{-6+=+Eh`Pv#xM_DGrgYxG`yt``#jT(7)1YN zS_+Bd{MGwc$6%O@$JuuvU;)qmo{*;)9VUGj{S0Fp%(sS%#DK|V4s zP=07hP)IyFb3YCIY(-FdrW$%S!xP~ul0$)&`tA__>Uu#UWqN)){O+J7&+)MAUY7oE@_Qukt5dRZ$=T0m0r|#k=_WY}szR6)~4@PNyX{Q*=rYM@7wv zJBW#Rnp4ZyeS&ZMAcJ_5SE48>ef)#Wf+Zo~5m8n{iJusPFV62-40QfAqW+G#9$E8P z)jNs%_`Ar;V07RaU69>kyLC?31Mhs5iM=-GX_6tIh z1t6#Zciah?BQl=1*Ecj6tB*;(O&;yu+zMxr9vF4Y7DYL1(rc_MhSpF8mtQ28vIaN| zIPpV_T;a|DKW6mxYm|*u-ri)I8`aUfH(CMe3w7B*R~pb0PUhL)(^TIHA@uwtnH+o8 zLt@nJCO)9fDydSh6*$+*30XAkqJan1WNOal_sr_aOuZD0D(}>l6FC;!s3`+7TTilcYCn zVcYrOjpQ>yTHg_{G=yY<1Bzjj_iLnyQD^SA_QOhzz0nkV7br8hNAEqx@4}V$9#VVpUOrtY;fUWbLXs%5?o)h26)YQiUr_&|D@VtSY z*7~)RfD2=pb>pJ2|A)P|4vMR7*L<5`NpN=vL4ySk1P>0u0|W^jAXxBV4Kyx68n+<9 zHCPB9+%33EkZvGIfNr$W%zD2)Q?=*R{@%T-&dfRAIW>QD6}47DuWr_Qo?EW_cS)+- zZ)#Dr3jDU9!=_p|7dbBJ?g(5X7S4$=bVGkXv=gQio7!Gvy|1mwjvH9==wK9N1$Cd< zMw-Q~%Y+7W_%f)i!XyA!Rsq){yN58;)qhA6|{c~4quB&gw< zDVg=tnV=Up$;UeYKnS-@c zmccI`0_qFEuL;Q%+0OZ~56tLv^Av9DR5C3*ZU4CMz1q-8KG5QKffP2H{KP~Ueq=|x z7Vf#)FQd*?6<jA!yNw`X&g2X~92S-9|O7D#d@?(Doj8P0Z*5+F{Qfy}+?T-$v z87x%65gL@F$R+A%m3Y#GyaN^_7Rre{+}kdbl!Sb_b6)o0RpsMQ{s9`(0Mb&Ndcf}K zpSu_T<6N=yO{rPv$!W=i3Mh3~mO#Y-AV3lw4qY~Hw+r1CNaE$%`cqC6a*@`|*VICF<}ZU|);`b^-0iy$9* zOp(@^l;83iD}pAFT0bD5`N1{1R=v{wXOyd0Efahd?h-~8_YO@0_~^UXiM`v`@qOl} z%=o9E9**lH4C*~GtQp~niQ;v-<-Zakx&uLGPY{02aGTx$f@KIR+BN%NmFC@#JvHxl z&ZoFc#-OX~Zqc~}JA*%<#U!4&l>_ERuU{FL+yhVTEB&Wdso!YQP%kd8DMe0*EqPgGl?M^ktMQ7uosBT!Vi24Eu3pBj*O;qt4 z_QO^$u%56DAwXB!I8P6h3yl^`(U`9rAeB{y;Mze`W-EOQ92}w33UD0Ngb^CxlM773 z9hLQi_>3wy80}LX%j0-_-3+ShWaw|~P-d_@mPw7&lzh`JANs*>IFQ);{c`kVqs~x8 zq)il52yy$Y-=A)3tlFIHzMhyz8;b_ztm7u@$xJGEubOz;tV!@2)Y4;sbK>pH@v9Bj>4Z^tZ5e)^BVXOUvvzV zIiI}?nQL1;oF4GMkA7ASNz?CWY(|lLJ`_PFF+s-7h_A9sg-PA0-51p0L%m~ zd!i@-D0?*A<~*h2e6-s{y@g;aL>%MJ>@&L3=q)jD|}AK19$2 zlh2s}@E1S+zR4y{WxtzL`JH0OouxEv(k|gMSF{!;sp3!eG~6O65pI)Wf@MP(>Vp8@ z3XH`0a&-~x-e!FelKDw~(`z$oUo}}4YvpFea=P9nhd@}p60`F;N4V25dtIQ?W&a22 zoRd)>EXV6stuZm0T&%yIwer+8_uKcMO&hLD{<4My-1`36>r(lA=np9IMb0@XFsCuW zTE%kC@CTG&18fXum$-M)BFlG+GRG=`Q&O zC_cfX#|To>dG*di7_{`?yZOXUj}-#G_h9a;H6P=@#8DDZB*!p9@NL~hyiR~6W6gXC zbo}ro_OmGLVPX{MU1n=d4M|obTe!d38Cn1Fprs4xPyZb=LL$@ia&m1fo!+m(EHMv$ ztHEYoCh=Lpo+O~HoSWm!4N7zGFJc8zBy$e@c=a~q;6Nkno^p6Ne~(x<3_#^;V|#|G zJR8LDxGrFN+ChFHcS5kr{g`7A^l4#hvw;R z_uJ#l#Ziri4(eci3|9H&JeM}2Y`zVPxZ5}yH})XMz*Mrr8tTCeTLw%DRV+QYw;to? z&vNaH3ZMYd_C!{;{f3>i#&uAAz zYT=^B$;4{}oaJV@oE$8jgn1j$u*?g?nI0|^e-F$}SOR{N`Sl4;C0BVF6Q_9=?d!*{ z7r*7{H+RDdEgGwHrKu^d7(YfEAdZ_caR+q;T%~+8*hR0<)x%YMN z=XG~RMTuX^(qAL$)rE$5OlU~jKE2bOlqE+r^5N?}R`?v?E}AtH4bs7S`#C;oNb&^u zMjx)Kq-2ii>tsKKF*yLwA(*$soNcME4IDY_c!Yf6X)OU zu`L119CxY@0~XYl|A1r@o8lI{?i6##03Poq(-jZUd9M2>dn-lOACO;rah<*R-L;>_ zzm@I(BTDbT;4y;x0kf5=Iz4~+!u-?F<*$(D#_P7^4`}l{q~La6>y|aV1ML0>ghm3P zOqJ>%P;S&s@ZX;YHU9gX^xyyd4=-`e;*J&Pyj=YMu}=Lzr7-%x^*;Yc_VS-(oBu(` z`nR%|+5dbOe!(KM{{V9-iI^~KcSalY8R3tGqTvAVGT`SAIjiqoj_)ow_JK|9QHl>| zF|kjE+FvTaf4$egXx^YC1c)7Vf0H|qgZ|YMiGRuDK^->vRr{Wf`q%%fzt*#@f~|i( ze)+#LIry*st$)EJq6M1{8^lHq^fP4t)v||we?5PnNzDD{YuG<45&SD1=h|xgDPvq> z$lJbqHGv)VH)3FEo!f5#oAD*Lp_Lt{54a<4KmKn6t}>}{j;(s84w%b6IPsZ0@tGgK z<3*oT?RHSIqwO|s36FGBd4<7gLHaY8%yHCR;=7as2SI}{NOAR3cKRh*D>%TTFlYyQa77&r`C~4K=Eo5vo zk$j{}zK-(&t>vfFL4&vDBN~3~A7u>H&ez4r6-JF6bEMmWe*g!t+-V>mf*BZ zwVPWNHLZ)gP*g5?Z;(-v?q%sgkK`hzxXG8JU@#~+K0 zkcOh5?%R(^+bZG`0WC(__Qu{#{8B%hxw9YRKdX|U1uf*~>29dwkdgZDo!!e~Juw`4 zXdZw1b2X4PKkwviWNT!JQW{OCBw{X;wZ>$CeE!+}ger9bF?^}^WDJX_A0c`*%}p|v zt+885%4sy%wSe5N5-4T0j7_~WGg3AMRQiBR`DE*=3FsrXmf3rzdPO{}?;eW#+hk!f zzi(~t%~FuFTC0brybEfMW6Uu-(>dejcHpTkj5Rq7#(3!GpNx^Gs2>aG@ctF$kxkz9 z@U!8b-=50N;9J2JCgB#V7pPDvj4N%d8J(Jv%e=QCYZ-d=(uVPrXF%PqT(Y0$1<@%C z!S&g2W#*}8MB>*JqaK%#qo*|&!R9iHg ze;$NAa4rJbO#-zO8a=MKG?s-~@@ykK<1~^XCRMt__JEetTmmlc$Gpa4TQT{jtcC)K z4{K=Nwy7|N*QMv!S-p6M#B~RRT)Y&YJp4pgcX|2vECnfs=mH-_bu^RU?53oDw`@Ko z$6%wtvW9C+ep-B;O&yEP|25jNV%{}6W|1Y2%LiW@`wN#iwZI}CY@jE*NksQ<-NeD0 z-Q*oBeg}$44wxV~)h*{qhN=vTBRz@5>VA1FW!uEk?6Lr6aYJA|1EQ z-^?J~0A$h*C&rAPyg))cbJuloylU8qnW!SJ)AS5566Z%jvh~f1BvN|)z0WrXzGJR1 zi4T6Z7&EVQ(j!C<$h>n-rqO^g#ly(WY)*PXsuUAU%+j-afxJ2|KRp-q-y1jqi~y*< z7Du*gNenq=XE*0Qur0(`BHbis;mD9nH;)^>{vj?Udo_VsTAI2i8Zn%Z#^srUn-zPC zYL~~)eEEK0+h7kHcy6k;l?)>Z7rmgl7h0yTzA^EntPpjSGy|VrN3x=3%iLNL+LL!t zGPY*@im&~x8!UEpJCH$9n6&BViFZ~RL)i0w)YF84N|Ql7n=;W=ec9gzpqQw&^BkZw zybF)JsFXQPq`mc<2BuBBm+zW6?t;0G@&4v4=e?i*EZ_Zy&*t-tKOook@IWEk|6V1O z&z^y<))UNj);AwNgBDrg(oBh0z4?gN=Gf;#Zb(`-tuxYzF6!w=ksuFy_RPv%(Q(Om%~vV~9Oh4gV6jT}m1tK)Bg!Dm?F+bJyjJju zbtkUN_D6KQ6M*?x$&AiHWKQbc6UU3>7=`*LqPq>wcg_ zBAVmP(0#b1M%>AW*jFumf90Pd5n#JA;7EPlZi&$`Pm@caCXmflpEC1|8nR-l%(+tW zF_1{kG%dE(Op+vT&x|<(=X>}VZVJhL>?l4JtO;DZEfGdN!yil}K%=fa4(o=g9-U&I z|K|M^SkC$R^74V8;;*lCbdO-LlRj%zV9d?O-SeuwY(q*(N+5;N#&*_#6V<$!=?uc( zoWPy?G(c|1u)#!Y$M|d_AB7X=Mz{Pk|9gB_=-X4kDBZWWouv+09L-l!n!{fzA04|3BNRLRFclm)c|WXxskgSI}h;UNcvYIO~Q0`fWES4R2jf* za-|cg82KP8Y|?70OnX_@-!vFz|VpY)MWxp1%Qw#=1CEB)a3WYDQp zjVz^YDYGm0WrAAmi_Z0Gx$LZ2yCcVn$Nb5F3T2eK`acKk5=c=)aAKs}%leBCh`W$l_X4o+KeLM-9}b$a+NZ zZOBbHDqcabHwz?PuOy|X`8jxLg{`M)IA7F+u|rOlYNy~`RBlr1Ga3fmxqKmoqzYXz z1M%)(X%~z$7`)`&{<>iegW}@9+w1SWQT(PyU+%semnxkPEMzJ->i}4D^IRXLVy}`@T-L741Wml)SBM>;VJO9z(GL(* z8DZUfMRaKk(b1_pe*7^;W4PAwQo;t%E>UrQ+2x6mOg*AgVjegGwlF{G6mH@}Qe1)o zv0r~}9=!%9X6)%+kFa9lDJkjHxcBAOvqS%iE2thb?1byKd0q^>m*Y;L&#{5Zu=z%T z5~D~$2UXHk^6qyP*R~!l4W{zYg7c|xK3 ztJ>#GB97G7Q>c|Htw#BAV!MeLzUu%mC%uxN+Zeyn&mI#51AI1umP!eHmHcFP*Bl}_ zu!3mJo4*RWNR?t5N0mw5>F4FR^uzHyF&Q*$?5^5k%Un{=pN9%j6S6&&``J<~#fkYL zDoRvHDF5q=3R&DHiXV`XJ_G-U6COll?>V59+O6H77cG~TY;sbgAdh6j;kDYe11g#R zJD70aTKLs*C`#aUitxr*IaIFX)N9VPkXGqrHazqbL@s+jPXHVPH ze5Lwxu(wV%ochRHo%okO1<1?N0+B-Mz}QCSgh!t`kV1YG6WiBLw2T$EiC3uE95jK& z?aGo(8Z|6s=X@|VURC@cPf|}!Ds!t3J&XZ3L!X!sOzr>`-H$@SY)R$rrQswEt^*7; zSJlF)4=mpwntyo(m;=6rdXCCk;b1=BQ1G+lAxx0R)y!ZY%a3okL&IO!NfvRImUrw-|m%N zw`EaD2?1BA9sP-okNNEgj60;uS?)-;%yxr_=jFjcABdNN(Oa-JSZuHpx769Vg6|Ff z`{8$*UTV@ubAf!58V1^7v!RQU*n5B~(>Q8sp@i zH;D@_;++>jpjKL(0yppoS{sq@jIBJBDl6yq&VO8#2@9GbG6rQw1ubcbEeF*U%CIno zFxcU>nEHkJ8&PJzDMX<dP$R_k=@zLfy&kbSeHsBU(p<BNga$}yh{l#z0lW-*3J+?E{OphGW&b|Cn}yzbFvDWo0DPy)c9B)c)tNpQYXX=8*B2K!eG zCxVwph6R-phAvv`A8I}a7&l4nOn~w$biR*&F)*UT$%y^B;*)bGlV<&FHr!7)g^9wku$<>!i0-K_&cKddhcSzP;nyCaG~&gg2mOmy0MRit#1uaKD;9>`jd z;=?yid^fZFj(cY@jBhaYr(1QPtl=A0-+Qo65bM9)vJIDN>cK%ibInL~4P;f}$ZccE zZ5&@_s-WY9Q)j=6amC`&&4)SmN#VsR$F~i&O*lKPd_@~${2%8T>oB$hNTC7cdux<+ zIrrN0q^d$$noOE4t%q9`k}+hW#r6&EXdpSg&jb3NRJa=zwNJ2y5qrb*z0&Andsf?6 z;9A{BWUtXZih$9*3|p1^C)UWx$->oX(gzQ5h;Z9Hv+qqq1Av{>gTR%yV9Jb`QY5f2 z|0OI7K@E5k8x;6D7TA5WPt)2`et77C$&xMM2F6{4gvg>>0nJh`gT?Qm{N3Qaer|E{ zPyX``>O7fr@hb?C3y%rOMXjyG?q=)!^?}_fLe~8mQm8H?lJC9)-8gny<2)e z;u%#i=K=)*8a0C%uX<;BnF%ppu_8O$b>T-B43RS)E(%Obdzl|SlW{s=k-ISfe_G*> zw-Y?zJL<&`7H{o$XQLTDz{e4Dvfs^r6ANZsIL*Z>-O8XQ`;l{aN^g@r%cl4=v-W}& zXB_oGK$iX}!o%bzi&E2Ux)8LqpALFcu_vvqvYT;pGVdW1lK|D3Zq=&#xngX0;$6*< zxN;bl_`>x#v>DU$Pzg7;I;O*RFgI=Bj?()bNmxTXG2!G$2IxS> zmWdWp-m^+GZ9e&Gd8B-Vp+Jj1%uIlb=@ZF{M>Q_Wu;+*x0>CW6WOYEEyteoJGt8c) zi?x#nc1V^;sEJXnaa$;9UjlLKzA_9%G^W}?m3cf#_%T|`_zJifA~!kDC=j%bzYz#W zyiQRsf9Y4Rgk@Qqw|2|B}eh zSl4lf`BI2PpD|O6I~|x9{a(d-x=N0VWnmE`3f0I`DvW_73Kyx4PR*fbX_`N@w&io5 zGq=8=%ctAk=p5G>q}a`=GlW6y0}vX3mv{(9=-!DW6`AtWR3r|d@)VS1J$VKF5RH4l z{l05pw1A#Mh7)I_UGHwz1I#2%p$;bqc!}x=b>65&5k?#c>x?tAE5Fm%W}$DyECPA| ziuGqi{{gMvMV3I86;W*G1+ETX<=q^4@>uwOUBFZ%6XN>YKW4&U|wJf|ogShp6M5t>7K}oPQ2^0Aa<+nZ0`3IN^4e!|; z6PvUlVmoeq&}COR7!xhAo}JJ3U1`QSzgf^)pQntA@)rjSFyS}Q&HLdB916HsR*X## zH+-2AYCt6RdK`gy&@$a-+~^p%BiN)3RdknDqA*YLxR1Z{9W*XEE0(}#?e{pGh!EYf zUR0c3v~sV;xc^&=7?MQIQiwkC>59op@ zG=T6(lG#q`^p3lA9?`z=V&&`Gb|dovsgowjMvQ8hep*XxO=i!#pod3$>WJ z!I>{B-38b~$_^}++)6EYqUX{@P1S7=nh1E-Ej(7JVnscvfl}GJM4*Ls`ES>k&QJV} z$eqrr6D4&}PvL&yRjKE+Y%B4pT~_th+WZppli66aXO3X`sH4}gt{d4O`@C)Rpr9O9|xs*t??sLDf4FRhf*a zd-Na-DFT1k16BzucS5(p(TZb3)#zh_198A8cAV_Tl~v`hOERI-s8P89~oKDtPNl?sIpH+aeARu zVHBkuLulxuB__PtQq13Lv`~e4z~c5Zk$11bJ!rkqo;?RiJ+8YTTM`@YjF1H^1W}P z#YP$d&O~Vx{ra8G-({ulS5NYC^kq;*K1yko;d%A|6Fc3UYajr;QJo_5-Y7#Vpev17 z(%)oX)V3z*B>ap8aQ0~Z_TAfE%z5*kNR!62w6`uWu6;sxo`;S^$qz8HO!BtVm}%J; zzuu!9aY1UD6KqL5AP<3UT1wZSjvvq<_5t)6`rfqS*KlyXeNEy!{b7#e{rN0dh#4kI zs1Hm8g8|5~4 zj!JKiE+R6!I~KHWp@wSnd`egs>zSKfYi?K1Fe{Dmu6 zd{^dSJBJMHOrnkK_4L$~xkRqtR*29hsqAjlzBD0J0X16^cNP$CCX#_SB%Bs3oV_Zk z@G`fVIgTS3R}FOatiREWwSJmb3*Q7UJqQ!~+%4OFD~IYv7d^i2OfXWpcg1635o^Tn z^vvn_k_D6mwqa4hio`1$q2%$_+0Q`F!%gRN$o<)O?kJ`PGkO%i!&V1F1coR&<-@GT zU(#;}N)VCWc%^l(J#Yt8zo*GrCQWuT z>VLRIDy*7!vA8ZZ?+`fnPv$I$no&--(qIv>t&l87P`J3#E0iLtsjG=mj0vdh0R!ah zuRfD~ytao+=TAh9dnX4Yiquni_;Hp%SB-G3z8JuV7oXH_x_+*_vcFMOoByq3zRLnj zF|MVVR|(V%)fnW_CZUdwEGaZFu9%d^FP48I_)-oNFUxiRHKSzqFyKg>6If4okL4>Z zbj`-jMQ_C6Uu)lWPO5Y3rLqsJGvgIM4WMdL5 z9JD?AfRk+a(N}^=$XT`Ebh{vAzw7xyRWwRo%S55$K8yL2ti$9D|3lZ+whPTvL*!>z zIV&EVDq5f-kgP>Ul&o4y?UKl-1sF36h&ay}@5{^-KlNUG)cD#`pd)MiQ$HAl%Ekuy zU1FMO(c9CUw8pCQZ(8TD&b6?HCBM6O`}}~WxQj=T@rqS|=Hrj*`8YYP_)Xj_CqjPF zD_RhGz8_q-T3&!|?4iXsP;0nq$YZF9r@{)~zcj3bs-Vj~)*VGLie*Izw6vonA_|gR zFy1*3Q@eMLdY2q9SXvhE2M{_O7zi&`RHg&lP7uqDuU`RRAYa^&Uoo9r?TU= z5BNY?Hln8y&Y0*9t*_{mx2bc_4|+u#Mz?7?Ae%NAZm~o@SBGo%CL&XsNRe#!JPX+geVp%AqKmVf`-tBPhJWWv|565uA*`I8|n(AoM# zB75SoLQR2|V(U>SfWx)A*0g&EhMlvO6K-aO?3x&U-=}A41L(3ZcBy|rcn-pzh+dxx zH|4h#O0&sKaLtm~?m)patr!{NJ0{z60LM9;`LV@5(Tm^_R)Q1DhG`OrxpyJGnD zMibp^j=b*iBouJ1K!;_|YXwtCQ&b5rbN#D>c!8w z;Z!DZ;x3lVpDZz9Q5RZK*;E1aw$cQsi?DFIp7}~YdO@pfc|xUGop@E2V%vKKP-Ar3 zdZh5J+rW~Ak)0QDVWNfQ1W^-vDg83Dw8n%!3ZKB-bZ!-AKJ{F&UPd zP0tx;*@P)946!}w`Fx{1{$)LG65v>=8V}FK#(qvBT=vAP#{+_7XdPPFSH&j|cqxqn z8u_9bDo^;e3lnj~`~1WAiEm5Pn;Sq$TyHHKMK*wZ@s8#Vn(`@0jQ*o+KSRBYV@mxM zN)Bxx^11}tY9z>KVIGcVhygJ5nb>G$Y;*B_Qertp(Zv_C*DLVvtb zTgA3I0q*pvs7j+c zHc_RMW9h3?*y|-N0P4h}lKNGaQ{h3A4zK}ZgWutAu7oDxz zZ~3u^T9qrkI>-Q0da$2Xcl$GB4awdT>5m^wZcprF!QXN7gfZHRpxm=iTzHvClccgwoKHPb6bLj{WxWZ9oSSi~)>1nxQzt3c~FJNU788 z6{bG*;;q5(la>N`{73J2QHp(c)f)$|rGDHyw6tffQ1^cLg&hBsadvl&Q8jwQx)T4l zu6{$4q}wcHyFr?6=T4wL7O?)I5NTX8S{^yJ6o89j2H)S$njMcaW0;lHU;nHuKh;n< z+e@13g;n*&<+(QQ5EC(Pm6MFk+5=<7>xklMhsB?oc*KT@QuYT7YE#qtNYm5{wQ388 zs6o2JZ7Y5byw5teF%gsqvnI;&;EkkZ(3vq!>rW zYox7YxUo09>`9#18`2Jv2vVST;6g+rIe|M!T+$cAy%Ez9b>M!MJ*^qJWv`TV-wEfp zqic5hcKau^9>N|>2YEp^`uwNv)l*)MM$i{|_Rcrt*?De3W*}iASls#y66<(5GeRF> zg!Odm=Ab1BmMvAjyB3~J5P%EO<$Zo(*PQk)DR~g*O`re`2=O>j3ZsCOKhE3c<2LKX zsJaf%6B;3+v$!6gx3B6zhJ#G?nt5M_iWbo}ax>dgth1zYtPQ_)zQHPpI*R-PziVA9p`pqj*KQ?)WT>0;yS=AqdKDIqfkRP;J;8qjl;=({87NTh3F4^;nUD)CVP zd+j}(tZM&U50za42RN+!eQ=4;jKj$i-pA1LAR(>`j@(?Lcj%<` zkq&BGjbh{XwL3|}BNMbI#m*2dE)DBgOc2rFu}>^AR7qS*Y8F6#v~SjbA$ z_2jz~owUbqJ3Nx&aoeEkh@nk%A1ZjDht|lh?tuBgnJtCTf|;7T)M{{WTwa#)hPeWA z)CsONLFX8@<>=3?PT>Zcs=Pitxz|ALzCKR{m~i>?qvO`O0RheXJCPaTpPF+mOkryTd@);vX&F!wzJ<3Br&35cJI5q4F7dhn&+0f4XS_G> zuMEoZY5n$xAT=$64DwY6C78h70#rvjYI9<5jNn9g?;F0aSpU+Udij-%ei%6 z;vwR6G!K)M^WC_Xu-lGZ9m5Mmju2la8qjPs_e+%*0VH{QOJ~9oH+I+Nn*9i6lNa`-?kl{s+sZVu9Es_GXB>p66R4Xi zO!)KK=Maim`zgPu4gZEL zh)3V+ZWmnRzcFe}1@h%TpeeFuPNskA#Jw4}glT~8sIq$=o`5_bCs{7=KH-5=>N~cm z4AJ}&Icp$=v$&TI?zqSy#C`=GnHu(j*%6(zmHTCpt-)>=t|u6yAjq@dEfwg7la0fw zjDy~^rOKFT`EN$g)(h1WS{CL%@8%FmKe^GFua8kTnLBcSJ;%mBfYp`!?WSoHH7&9u z^UdeJunY-X^X=yM!s_N`YR=}WX@pI0Ihe@y8wC8~-ig^v%6cSkpc~!yEt(mIgh>X`{G-jM%U79LbYo2q*b_zSowhD*HaB z+Pg?ZT=WRs{y;-lG~2K#m^(byyjcYkz%Gomv!D%*fQ$D}DueJL+tdAA9i3SP2d78c zy``;xVNx4|&H=w#dz54c#`&lA(l;S&2D`!DbEGv~BZQOY(MxH4isNfw}bIZH>hR`1f zT&zbUt~2vn^D(V9sOY{NM)v?p+wXnbIqN&A`|o>Pbj4ZXYa%`kjk_*KT``@fq87i- zojwKmWxuW~t4e*cW(ykN;ycPgRSTiFP*h#347EtlkAe{B%LOJs;-rnTmBN+BKow1jfo*r<+se*MMf<@&V&SF}$ zaaVMc6KY&U5!vFqn)pT(-^+G2yx%>Oeib4>K8~4nYey-f$OvBf4RkO){+UYauj8C= zdih*4)cEN_5{XIxGW;5xs&71x&Kfa~RXBO*;89SS>k820NKo1)Cq8j=IVax=2gepMMYh5f|X@IO4ZKja%$} z^JTuTjHMRcR&25e`>Dm7A#P@3$-lowjxm3ufUd4khbJSS1@q2p&scgzsVu@QKg6sg z4anMc5cxmd`#v7I)@deMtAYDEmh!Vj5G*KPmt5SuUsYrX%G4M>j|k}HAnF$F zjX2D$^$#+%NSP(Zr8Sl3UJF>-wRKoWn65uQ3S;c9QA$4lAFGKZdGHpm%5_|11(K=pRqOtqVYarNVgWH2Uy!Qd zFS>`#tA_8*91kCWHG>1CbbAJtr0X|E<@gU(PwJxi zMRC%fhtfmfR2RL0EXz-kU1MPTMx(7U#V z&%J_`^8KrdX8_Xp&%2QoHV%>%wbHPH2T8&XmT+JMksM5WC1Lk!&n`GU=x zBxACWzab4`Y|cLz(^X47Fj&SsV)@nCMR6c4g9Zn{ zbE2gwP@(?d1$XtdRLB-q3pW>t3}&-+1XfVCKX<8iCf5P+^9S;SD?!loQm!Sai?90Y zIU_{Iw5z1!B^efNFG{~L{2dCXjN&MgPw{usk82(EOCIXPC?yx6KA*}zpq|fK+UQKz zYK2a_z6}fURegPVkSh#j#}kDAp-)dc8A>H9E1Ac3GAPR#Ch(4H~xMK54M z?qjF^YT5%25J2)+3iQ(xm0%Y6m1AW(j&>`QPHrYM01c%FFrn#E*np!l==?4prm_OACkzaG9t5 zL-_-GOHVCinD8G^%y**5vp*p0p(0ytocM9FxK*r9gM*}{wuH)={>}{x@R>PepvpcJ zoxb5`)9cT;xG@qMd?my<7vJ#X$0C#( z-5u4pG}F0Xv8%(2>Oss87Z|)90Bp)Rx@My~k_kijR-*robiP{TI^5Tm<6Cp)NJRRY zL#&H8*K?owNf~-+uDv@tq*dU={H;u{wYtiLfEMq07KK!-{jg&l5t;uK6ERaS?UYDk zsPKg^WW&m~IAz0)Rkr}YTuGkLcW&)~*YVt$9zyAj{90U1B}EJhLFN+LzeekzfQ*Q! z@{ZF8-J#bqYZbNXRpaeSY!7pOvp7cO+ULt7LnFcqUJuVhJ?4Iw2w#;bI*w-e;)vjF zA{%ovD@RD|Fr(ikQG(ejH^dRnVtr774HLVhbBm^?Art3%@a5tGV^7ny>uTV0CL9PZ zT&B+%KcKslkzTBe-a@gd%hC0bv*tTBmuBx@}bj=?a`IrvWxif%hE?ZEf@& zuR|=GJNw*?CC!-8RH)&#ZeyGx`aAmO`b0}C*tF>bh75sJv7IkMN3u-$8&ik+Cg7ii z?`B5IC9)Hd<_*q$dE(bkr%PT(T0W*v60I9AQ$zg=p_pH(~va~cmv8(y{x@_fIP=!nU08EZ< zRG+Nwc+ViXbVrO7O+F?CUBgucM$P@F7$3F8A^3udr0S-DFxi2U+VFa^N{)dLXyY2~ zFfm9oLNaz)zRs8}E`N#mj7e|PL@|dRfGdZ#l3RYCw|ZUqIxt<=-+`=?0m%)QiCWT% z@Mm6ny^JBEwv-rWorjV19S3@CU?vU4F~G4Q7D8nP*2i zm8>&;6n)}XzB?Qat%(Rr!O&}3XMAlq^$Z`m`wf6^aZq1Spn%BbNyCj3$`jts_`ARl>LGKSgZrk=zl(WIHfFfcYl0ZhF49O< zV+I- z1i>3^qlSZDq%XEO)6h@ge%Y6ymXygy<1F{#xu0ZC6iWg$q>4n|4G0b9>Q{fP8{Jv| zCrkUp?_HwpDncIx0!*%owf(yTAI*dfDH#>y4`YuX>Z}cOUkH`79b})A%txVe7O(Bm zCj&!tk0Q+mY|UhXZaFb`hc+R3uR&w{LRYW0CCeP}RW2O+5ddMU*PZpT{iitjzw)U6H(vjeSe7_`#gpvd zD0@r0QPAU0(cXEo!rScD<3+4hd9pg{F<%cEY%xf=BlH^*{~LR69o1Hx?~4X0F2$j^ z6?Z6ZZSkVTEfkmH#e)@hheFZfr9h#$yB4QVf(9*6AVE{CZ+GULby4A}?P4G{hw%5*)8np|3@mAGlv8HXW9E`vZkJ$APM!7>f;U{$<>! z;KPt--)Dv7ltKKjy^hochA>Y~*HuAaUV~SRRxIFEY3Yv&4%FM4$pJ659@xBLQ50ji zxaFwEk#xaR+L9y85oe;#(Fw+J3|SA(QF@3HwHB04Ul7_;h?yRT1eL|wKke8p$el(} zB^Br`DPc$n9R%?!&ur5r4Ehodc1O%1*9V|HJ70({eGO^YrA?$qYKu&qo`!X)EA2GD zr|E!%gv!xjB3w7RZ@Ja!X2tBDy_6FOi;21?d zUe0z{L@L2;qJkklE&|(DWfF_ar_X3U`+0u3-_DkK7QfXo*n+S?={oVK(~uA`LZHswUKI1b==j=Kz8NqsP4!1H>y~uJ{AVMhrF)+Nwz% zh8x5Ibth&{CxV-ccP=unLg~YhfN33_eHNG~+jVtVNC2Ro6@Z}cJyOy}HsuRPPx{lV7HnSi(-R|`PR9rC2^VbYTk35+QkV*akMuy9%#^)HV7XQ9MaJiN zWJ9=ip(pWru3wDehB`5{hB57B+0bZkK*+Kan9>kojE{bsY@#bKcf))20xmy~PrD=; z$szLSyItiAU39)cGP)1%T*xA7Y*h?TOC=TS6`Q)K8QUal(TT`@W!16)J3Bhknb@WL z#t6_obDK^&lU)5l+pmy5Fa>yZb6Ab=NX_fl#fhW|p*Z?7xpqmD9ci0NKaT+UNqSvtKODkGc&tm)>iv&uj6#F-%2jHY{SdS-Ggl^sIaw&Mf9^1 z{nK(mTt|l0WX8IA-`~H*W`GOq?`t;4y?*{I5@-0%mF*8G%mFD6cQt_De+k~~CjC0= zXyA-nc45|Aj7NY|cNBYVNBl`)Cwb2sO2Zu9{;uhVpm#ZF4_Xw`4QCZYHy2ilMXp*$+%dZpS0K{&vDBr^7(f_!c?|?D$}M zUyOS-9t|oc5*GH>)>y^@*6jBo0|f4-GVSp0&EdiKEyAvJeo%_2Efy7wAIHquLGY0W z%Ka!{H;G?XK$v;)4kxMq7lDS8E_-U@-67IXQ$vpv%W8D$l%Ln$X@U|7&t_{aqm% z?6=`bYe|}_wZ1Vs{rF3-o0N`yZ-1a|xJCuz+za1IPXo%+mB-BOYpj{D7mkY>WO}== zQc==elqUi?_`*9B{`BygB`D%+==yc@v5O;rL}YCFVWae*%zDV(woV7$4;(KH^bWW6u$re~raFx)GY+ zdAQg`bW1WPL*x6@8S~})8;w8(me|eR=ig_!6HOdYqJMeDUj1N*c5Og4$o>(Y+C!89fav;E}(0bqF@B(fIKWRnv2}h*R{} zEHMQ&f58+f%D}B8TrVmVB5G3vm1~-Ta-fh};MKXlJT#ks%9dVtRf?o|wVcPZ(kT7)%OAP7PPg znS#Yq*=?W-&126v>+?N_wZZD|?&XOXP|j#O^i+s=w2y6gM_QmCJ-!3V17{eW@UzY4 z2#C68wXddj%eNz5Ca)XWQQ{(Rwzn#5_C3{_5wh5gs}jFK+KA>@URserYsIx^oN2fU z8L-~ok=}i<3b!cH$m1qAE1OZkFS%@dCyQYD3!8d5K=wbA#oG@OMAHq^%Gc!B%~ezZ zw<(g>-Qz<<3VPkl)((lOPFzuU^5htbpGc5=kG&LjV9qtp^?TiZDzSH{`Br&}*bDEG zRS2P+Qbi#lHgX)EbF!%td-l95koSH@UqH&9_VXvh+OIvHIi~?rC=eZFmUKi`Ts%}E zu|RNSn1jE|G-UcSx-&XK*Tto!!h`Fv8gn7)WH;~a#4TvCUjDw09kh~YrO?d-$fWwZ z7;hgx6;%7r$$9yE=AT zuSLqAMu`nCL=T*qxHwlFAQ^qpfvTr52;P9GX%9p=g3(J}zdm0oHey}9T2NRW+iG6u z3Z?e2k~-Q)>hoR7Dm`7L#5+cl0%Vk<0P{0f_pLy1 ze^zKTxoDNBoO@`JwyEe55qy0!D-l9L7;^id($s+yzUeq1yqOOZHlT~ZV#w()5Ir1J) z71R`Gs}8_ld%tCeIL?v|pM8?%3eb|}wW`@9RWNh!xMq#1&l?2BW-=H~xbki^uQY2* z4Vx21l<`<2zx5p-%R(=NQO?Q-T+xvSU=qIg4(tl`Hv%-y7NOd*-hvvJ(j&vF{cz3x zU?ODJu(t2XuqdIEPjS1jcOtnwQHHV&0oJ|VIkK@iL4R-@zI?@aadlooPNeqcc}Yby z42Q<$Hz;@^M5=ttpubveQ#RuKh(xo+$>>ufmaE`L%KE{4aR$$v-Hu>*q$ZWU1O%@9 z3PBikJIY{D*qZvn3OyMUZLKS8b$6HJ3KQJ+GBk8fgzlT?&DSv3ak7o3;5-n-y~>wx zi!bT?#ue8oi{cS&XL+VznK)!83OO<|bn*gWKC>AdmGc9hmzceAq@kuXJ3dZ4P<68_ zE*WHXdg{MeGkIEPVu*PH-XmB=^(Q&h$Ig=R?THcByXTeI2Api0WeH`tAabouv$vLM z4*tTN#BaFMSo??1xIdww6T1%vdfL6Rae9Scni^dVy9p<_L&}^l@%#q)^VQJC=6e_2 zUo~ocFl>5y1Bntd4cyQ)oqmvIfu|Z|)f`z`QK+aXZjxLbF=Yy1VF3B3e^S>Qkd0xl zKGpi*6CoO~Ag_?pc&iWItn!#4XCgZao-;p-&%P#?JIyNsWz^crFLw+-BbIhBRX?ePo~52 zZRQr8?dIAyPQPfww|LHT()OU2dw9w>)N|F$&p2U3qxgf*Et^(boA;K<7?raX`}zUt zWV!CEk0#fyGhCYXN3=B4Bvl!h=1Q&lEKUBX6g){%#~X29ONKT7PnwWaY3z_?rMN4 zWLwdhpc*1_j6qyIfUOAcH>qGN{NtDkmEwa(9-kvOSqKiyB9&L2gy}joR56iowcNFmb6|EXBBMGTx@n01K4j_m^nl+tB>wsi{L?|_|+;W z)=IYF=Z#b`wuR39hyiv;UM-!!?Bld?L4nqrBBX_w7%9ru8UcJgNmv%wRH@2QNZoh& z$uX4h1zlwQJtl;u1!-4eYU*Ub<5{^jDws`++B8bT^TtfZQ9M zj;D+?Ux>Fq_h5>h7ZTuZiuT{|(fb;2_2+zyUVx#-rHa1$+}mOrzeid7PPiNGHV|kr zd2XyeIyls?&sW0sH)j8;-SYk6V~wYY{)@{)6g+}oY+SII^WWX;B1m8K@f*p}CIVf+ zTp3~renGDU+3lm!W$*#?rxWebc%cE9Cq_rkk`R+@PYWW1oPiJP&TB?AI@OXII|_|T zK%28%Qm_d?m@IiAfS~=-&lC=R4oE)A)R_o-yb7ln3+1B26hsSiOcwVZ3mNtTp*}T0 zPrEu=dEgWG=q}Sn`_dEpm#IXvIOWlxmG760=J*Z)1gd8AI({vykol>YKS7@g587Yu5j>xp%knlqmECM)*{;h5p}_VqMo*sX9Qvdl|`e#lvH z=l%HFkMCxkbtn2AqA=O$g)XVqg$pwcpSee5dI@ixM=Ug-uj_@(UgaoT~ufc_{tzu zh^c`jfLRi^DSZxwkfRvAxzf$1g1L=9zF>Ikp%9oQ(m~7^-w^xd0_vtHEzPN~EnOe} zOO+pV&IXWe5#2{@aHq}SL6cfS+0cudJQ9r=>+GGzQ06yXDr6uf-Z5Pc9p5QqFuw20 zE#3$RhcXA5zy|LhF@jaYQC=W&acMWMpD8YJoa0=y+oz**Ga=+N9PGiCK+P04de<(!Y9G_4b-jqd<YNkZYK_)SEJsA zNN~_+2=jiQzn&d>ZZmoyUr~&|Q$=@If%3QT0i-TVXa3qsUxRi5St5|t=TFyinVqSvq_I&q|T9_22NIs33j%O9f59`?O0Xx}NxN{agzrIZE zn92gFZ{*pnd;r%7WkvI8CPxeh*}`u9*x3q zc3pqER!SzJZf&JdTEoz))GxTNAj%U-JZ0Xz7?ZPhzpY%_L(j`ErW3H(kXox3j{Z2{{19w4LH*%_zyn;u_Qf1+iY$(&p1Xnlxm$E)Kp6wp z-B(2zaZd!S7O-$Mt@-#XK~CQO%a}BLK9+>#kP~At=KUk}jVhJKDWWl#TltRB_tLE@L6Pvmg)RX#7A(uP&gS4#=;>WF-o{>UFK zcO%XfmexK8FCRVZo*ObWV+)M|%X{nB;&4B6gWOZfZ&7Dr!HHc**rHDbPWD2F;o zpQE`@Vkx@eud7xdDr@X#DhrdtRTLVoCdB~-Jg@8joSXB8(l9#NO?PwaP=B3i zl()W^=!+x2L5&oB1uSm(C-WLeuB9H4+|<3|L0i5s8aK z&**%CvQ5kzS&UmZ_)bkmvAr#fr{U1IdfUr)=ra0&Ekk|@&J`bk!o)Y&)C_sA*_GZ~ z)lX9fJ+b#=%nrpl5X3N8>rAkkXNoRuSEQOec`r=}9x4O!8h7WpmS9as5YqAxRxr#* zo%4HeuGeaHSS?=Ov)6R zGSD3v68YZaM8q)0FsGUSg2%yl3PK>PUZsu}rs~1MwH!83!>?8CVr-RUKPd=>Q-Cpg z+KX})yjP~F-o4OZU>#dOoYF(-s);BhjBg|iyO)CV#ayU^VNPQU&@jazC{M~qKeY%d zOO;WUg{i*vkn1nLtw6G^(iZqk3)OI8?aj+N@GeNA8D1w{bx7_MowBXdrFByb&_fdcI|=^^27mVB`(@zuDCvDeM0eaBLrw~hh67~A~d#!1XU+U=gd^bLhoCih_C)x z@L4n)*F2CMo+4T%OP6VGAy>A_MT!!<#m0_RSbm%bJ zoKW3(#yoUraYMylSZTJgmrtmA&9uv<@=kHrhPfblL$~f3dOf;gib8kgVZojpJ^QUC zk_;#yAcw|>*T_24l-nBLN_vvAS9f6AS{l8xlB}C;mSMc5orm~1*lqf zxc;_Q;A~$+g_*^WMH49%|E30DFUs~Ms12@3&Nd!OHY?8AHsw}m1YA2DEsgqILwzk^xK~O`!1#Nh(pERN59Z-v{hfR z_?5(84B4a74hFy3Vt3jNess&Ta4WlI0)Xaaa8mB)D*S@pXh)cS0#^Tlv(&DP^ZT6? zKbqKTb`&d+i;q~pM|T1UvJ7vBkvG#v?*cvOI9{_0j_p(vkAu>pL@&t{Ls8~j56V(5aY`KVTZhXb*S1+qx;3WoR zQELPp%l;)IQG!qD&o<_H4&4i-l(o#4CK^I|u;uhpKI`5jemn423KdRxr1oQ%$?#Oz>j)JINs+UA z-xc^j;<-1=SB}(M&AsiXiO&I@5)wmXI_A144!UrVUcOMXi*r#9S0B`b%d;2Z+#f%= zsWxGa0)TsJ2oG`(-mjjz#_aydF{_=RsIq;Sv^n*$T*^H0pWgmegDdBdbuNe9SVCv# zK9QLK1IzGl5D&^>{xaa6?D+`tsBto$TSVU}ppxm=dL&B*sFud7h92hHzkf@+DN6!Y z9=L0*9@)VAsZSeoQEQ3I?PAPRsysC@TAAv~%^l$}d-s!2BONs2qDB#&xSN;Ip<6gK z>w-o_RZ;N=|L`}JpJx8CRxYMF#}3o8Y%t$>8J=A}XX)T^n$zPYKs5E~VGgK$cSM7b zLmAR>e|S9au91d6eLItXep|FF+x|Z^j{mbd_1}EXJMC9evR7A#`I^*uP;~dA(C)7q zR{w1p`TwLk_y2b~;{Vky{6D3{y~2OVf%_khk^fQI`hVoOe?n>de{MzlKWoO{R<)n~ z`>WZLYB5%^6t8VUsmjo8P+~TwQD^=-2~a5cIy16RT3()Lq!_p+vtkS3 zg~h`q5b9|{uXHzh&t6)la>YRUWbD&xfm8ynMhNBVS(GK?4I-ZMBsI?K?C^{dBnD5o z#TGre&Tp-?L_BWjqL``m8ZSB9Y=C;(xyXwhPE`k)AZuYCkmHxp2-)ZkF7C|hu7qMG z?{?*A@rcup%2W(ZVr1u6QTNO%zFQkLRUZ!UlOFzXmnYkNA}2VqePOBQ1|XC8P?3ik zh~22i-Q|kplzRalMa{3KQ8nD8oj!{aar@!#s;+xs>Nss_0^H0f*hw#~ntqcubDk

M_5~%#V04jmsTbE0_CHk;%^{onv%Za)3 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 z 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 literal 0 HcmV?d00001 diff --git a/packages/mobility_features/images/features.jpeg b/packages/mobility_features/images/features.jpeg index 10611ab12b3628804df8ab01f9081671db27f13f..16b27d506ebb872a44e1c09b966719b2d0e1eff9 100644 GIT binary patch literal 29631 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 literal 66291 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≫w8h5K3_ffbp%yAR4eOVXDHH2x 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 O_kYDT|K{7A{eJ+_%SEpM literal 81519 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 XFqAO_uv{VGGI-ELRsEp8H~l66M8p4q diff --git a/packages/mobility_features/images/places.jpeg b/packages/mobility_features/images/places.jpeg index b2be1bcfaf153dd50c3064b084e575e0278f89d2..e6fbfff97b581592ed5f1ae8e5e80a4ac37a55a2 100644 GIT binary patch literal 32661 zcmeFYXIPV6voLzoQIOtyl@8LSNKmAUfYK2V0jVLvHZANkFa%b$to|1_ppA z0086wDFGcogvSW*e*gg+K=LOJ0A>X2|G;etg#SiE2mpli|D=z01&IGfkLUR(==|;f zzn)i;0e}pjLwrLQ9^wpTC#)cY5g?H}VMn`Ad0( zfBz*yt30B=<0LFaq<^Eq7yb=0H)L)o-?*tPEzKh%tt_pmEUO6IeyE|Vi+}gf!Q08n z)5itk3jmjEgu=QS8g@nx47GIcX#xaz?Fm`zpFQ*Vtq|DL*ZYC?9UcoyD;~0~zv|{U zcY6n)XSa=v?)}F9{``&q6HaG-*CPN-O8n-vcTt#lGg7+;-0;!xIf6y%pLG8vcGc0z z#{vHai%&Nko_XWtBoN2rMghLhe&aAa&ftv~3XkI}7GQAx3orVOKlux9`IE-vfd-yt z1dsDLxY#@5@jW~){^UR8JO2k9?BVzOcYnig=_#E&O&{W)zgvm}xC^)fKEN};1F#1I z0Up2zkp17#%l%2O0YLE7fq*x@oHO78_~Ogl#^?Oz;DpC@0Z+gIkOai>xD0RupJaa5 zGrlYyPN)A?f8zhli&z1GTky-v%l3cf>A?ZugCYQ2ZT)AS@J#@qr2v4>tLz>r?N7Cz9*o(0{|q;mzT%a@rL&r04|a*FV71uFE0uK zfM^i_K6zg51CNdHHUq&M&NCia$s54UJGutMzs&>ygg6MS03volZI0hP10dueR0T+7 zq@@*Q6*&kM0rKDGc;lwD^j~EJ|13ijpVxbr!bC~EW`{IG@U!C|mkYp4yaE0OLi~q_ zkdTmwm=W-7SFSQKv#_!WUKbJ; z5tWlyP`s(6tZ`RUOIt@*@1cpQnK`~;9UPsWI=i^K`S|+z2S5X#N4$)TihdOnoBH~X zwDgQOZ!-%Di;7E1%gQV2>KhuHnp;}ix_f&2`UgG_4oyr>P0!5E%_ER&>l>R}-@b3} zppTAEew_Zqoc+R&EPttk|Ncv(|DX>oULQhYVj^O)-}(>`2K-i*mY9TBnw0LgF`2y= zJ)cZCIm4Zl{107M_+=lU7@v5LUu6=ILkgmQtMsQv|1*VN{NK{(9}4|LpGyQlNko8e zFd|w21gw8Ah`j(V0jciT(@P)Ft^e7xucxdB?h7oc~NC;`n2UkFK)7}Y0Z5oIpV}YGQ9JRdR%$p zO^yc~>d#eZhf4RBs)Mtu+V<(#d5$z?HfkZI-KFN+fL&eEJ}YXto7q+k{Vnq8WP`gSsl6tH5yAf?J!+2eEt=XBXiU-IFDK@DBfk?uiUP%jT!;1; zVuDvG$fo71%^s2z7iltXK1<*QJ`Y8Z$1lDIuIB1t!0U%}(D_bQXsBk#l&X4$jAgbO zf?s?MYEcx^D5G|V+#$QLweMbay~>S-T@kHffuD7CyYqqj%_(P`Xb^JxTjZhb*1T?` z!@xP$z`{hw&z=y$DOLHhfxxGI7BuaFD$O+c&y)mX2-G*;5sWcjkYLR&ffr7=x#x-z zTWAG1>h#F{=DO6g^i;&M;Z=fE$L8j=%~gL>2Rnw4p4nx}i%gIQC-oXA3C00?SiDe# zUU(#0H0h#P7tYyK%~>>(y!5f^B>{1KmBwqPr>5H2`di3D`-i(wT`RHBgG=BOHtD3k zYm5!v_)IFQr7r345tz$wc;EbJ7ZNm9TXUwyJ^SM$yJZ{7F5%?7n*PzmlKscY+O?pd zN>^6KX{T8As-i+MN$rs$+AhZ3^q&;Uo{YZ&&rL%guM6D z4Pm(mWmxLKX}iJqnoeKVV;TnE$9k)kV!nKx!-8MZT*?eX$0o6GjWd;gL(&SzN$4VB zC+7t!bm$V$KJs@t6s|ZuF8a=58v9H3Y_rlO^z!RJI^Wn=?s;vEuP32bd(gGU=+Y?W z-cF{E8916tfC>|5mDr8wKQ(RwTdgTk-Qn31eq!Xwc{^nq;E$P9eeM@JuQ&jw!aA)% z59M32HprGex}D>ZC3klpMa1nA-{2fS`eXCS z=e>ud%xh&f;nh_c?gB~msuXM1QUq85%sMKg7bj9THNkmxxkoc`0<&nbG<;FW*f((c z_@UcFE5=RJ=&*@(?&>V>EF!d`rdn)3`s-f-h*j74< z4W<-eJK2^~;l350saMPn+HFj9YpqO^ax7>_5)Cfc*qGhkE`9hz|5O=oc2T>bfEow1 zryG%w9^t^0iyQxSe#N%`BYSV)i@3;F+3u>XC@jcF7Ik9H)(u!3Frn2<{4`;=C6}5 z7_0wZrr@}96|CgNJNXNC9rQ{+gat7-%pB*@urNv(_<&nQC&gsc{|k8N|BL*~|qyzx3;j3p_=57pi)laVz$Z(O{J>3Oa1iM99u+Re!6R`m&;Z z`)4yNo|BSNu-TW7v?x=Gz889Y0H?^dlFJ`Q{rpifBZnH2{5Jj+>txM%KU?_rL!*F4 zZPrWip~ATpD|8@tNK_6d#<>UzU(HEFCb0J*MhpeWdY&zBBL<>M5~4yDg7{UR=isz6 zB^2>q@?0D`;6Mh2m!xGY;AyLP29_+jrg z@8X7iA;T6K1Y$FH4}EjcLGBs#KoS#Ft;e!1b=f5?4I^sucxGj<_1p-1?;-;WMuxuN zrtL5-?>oP>!}c?KEt?d<;`q?PZ>w>Lzv?<#;#;eWQ^$)drp^hD?D{G(YO;(yEAtH^ zlii_IR|J==HIK0E)*9!|-G(eTum)op&St-QYu&N(jGgSE3WMO$rCWxwsDR#&Qr0tm zHrJITX1?8~ft>0Mq3JWawR%251euyA6vY`3Ri#Gqa zB=PB1_v-HkCd%K99OMBuGS^I<7vLLvA=#2F!@05#8`nuVLs)IEg3km6X7%+}9I@eW zjMMrdwY!yM!g?_E!>^!bPjK~q(UkaBGgUKPk@e(==`yqb8rjX7m38>u#rk3RvK(4F z`oQ=6LPHz1T!3(yRqGRHQlt|%9_%e*nS86@;MMhT%tEaqr#d#N3CkvgLv`UlTk7-zng`**u|L~}wzXxs{CrzGqF zx-+*1%QfAhQoil4>|xaqVEkz5R+aIB#lp5*ui%JJjJ|T-(?zN+ZvPqH?F;|8Eg1jp zhksb7h8$`_uUFV*`~Q4$kRDT;yBQ4MJ1lbd&k<}#mA;Y`a=4(Gk zvxN_Ffeaq+Hbl-0f-}~sX}{N7V1`8eR020&pE@^8hFhjJFAKL{BlH<*<^Sohwc$1T zoH|U6x6kR^2BUa?0;ZD*Qy%LQ19K@c8!4RbR}jBj&=9MNjT5E27ld;eJoA73c}LB< zW~G3WimR*bYw1I0L1u`Ou4`peOu!j;ym<~A`hJG1Yhd*@Gs1g0H94-`l~;;yRaIR% zevqA}CL+y~eAMH62+8aZ$SdW59{ul*6F+Wg8#PVB+CMJ~y|6AZF;tKts!=40cV?-b zrb0-diBeo+f_-C%xBCvw&>zB?$hX+9o?Wj));Pkv-H{+- znEDisKV&MCxevnp*`a+F>)>3u)G@%N)6!vX`UFZy?yvow&d^9$N>dIWaY|GQ z?EOO}rJH@w>mOFBxiA454QMTR-i|+;S$<#j{%FzH1;g#lp&iCL<>!fMKehLT&Tm7> zY%hxC_le9=LNAfyRagk+&*xt22{OWBPZG6wuZ&Mw7tJZdf`J$a%)Z57;ojCCZ0 z8)i3M*Zq5e@CGtmWUz#yBlH*7EqV3{)qr*ZBZ zFP)lRJSVcN`*o0201f&SN+-8(*DH(3(3lS4Qe`u1uE5l>m&r#ZGJ=~zxWefWQ|kA} z`682jd4AMptE!;st=d~Il5uj-(!Qur3TVj8?0q!Fl3OjmwOhXu@3jE8Bc^4=)i1kg zwu*r3o%Uc*rHgyq>fSA~y-kG$PU=)J&xV*rBY;M?5odrXblyrg#3X zsP4C9hS%}tW!rXK2YxFU;n_YFDJEzvSWjW{hlE(ya>)A`CK}sYjWwUECBst1L?jv7 zBu2b4K0nsI$r!Q_Vw;PiV(k87`&{@*2~wn`(%QZC@F*%1A`qhNu67089Ibj+m2*vk z-e~eilxU^Bm%DhtJ-Y*v$c!LQ9B1CQMxj_3^SUA7PQ+dGOhRb>G6NOQltfWea{l)& zN8_}g9Ok>7>VL4?dI}Xh>xPJX9-+_b<^SeIZXG|g+ z0+I?9V7^z=0lI~%Jy9+XdZw%I7s3*P)0;P!x7`D+5Vxd;-H$LoUxYeckoAH{8|)H7 zR54k|6(ZM8o=*7W-jf-=R`H|lIyoOYG+`{9*CQuxuvQ*ZkD_1G?izbF4!eP7tZ-9~ z+`F;3J>e>_e7kJn5oM2jmX@sOO-|tRf!JOyHI@-QJ772GfDDO4eL2=n{MoZCh`u#_ zqMTR>DLwOgw0MtKKqr9FBceMu<{uZKO)maa~@R3x818ZUOWIsc-k zIw%xebE-E}zr>xSoY8amN?9rfgruB-fy(E1RVZ`kk1as6n#WX6*YsZ`AOawAkB0Z2Op{c6UGPCm=Wu>bUUUt?*;2gmZ1I6=a_5Q}ZO| zn81;1yFtD;(f-I&`j|&hs|$;3;j!Z1hx}4%s*<+SPY|3Hy(Vel#pr& zO?%kZ&}tQOksjy1P=MV>jfG<&>ozvnTdN=Gjb`M@!<`#HG^Ph8i}WxjF#G|;bFAlx z5$;SJ8n$N`PVuH9)<~s?DL*@;D%xIcyFp?RKsR%KL35cMQ);K^>U#<$mGO`>~eDbd&!~k#o;*;-u-&SF!X#A-VML~0r%KD zso?FF5d7k31*!ce=Mwli92&_)LH$3@q_lQ-T4oX+coP8n43}O{Uw{2+gxOL(n)@SMUQ(eH}|L zEhXp;$iD;hbAUVOqNXA0%je$HE%JEpZ_fkW8;@tb*RSQAF)IEW3pbEsJ6CBw%n=ig zOnUje^wSviBWnwcyVbK#Qhhn**iK&iw=1nMdX?mM}}r3pdMEdw*U6oz__4WBL``83a{YmL@n?4lZ_8 zP^Vy5fdYdUQ_`i&EFeE^Y>1 zS6gFyQIJ;EBy8i-CWwW*z&|v$BvV4t$u~TxB78({R$qCbwC>_;n!@T|_nt)Wr0Zc9 z*IeI!Ap$RDXQ=r0@+@$?e>9Oo_7!jBlsdXOB%n z3b8KiPh|g_Cix}Qk~J{x2u{= z9atrpy9)WYTB+)KsPCv2`)>xpU{U!$?^FMz)c+XFj>AAu1zir8Q@928)#z}};m%y} z>r3FC!zHlh3PNf_DRAL4@Sj^C-1U#?u{Hiqd%W{p@|VC>l}~mjRE?JaT>26~%Y}9) zqL?p;szVP)La_!x5)3}?Pigj&*e-!c&Q+;jWcdB=%9~37llgBfmF9x*i^_!m%zztJ z*ggG&Y zxBC|k+F0R3g3zzZvpDR)CD1!tH?FNhZBO+XUHdOA3ULepVes#q?w3Hw?@Dl=cPY${ zejn`!o&C=&#{abvjY+R5 z&sDpaGd!pioyC{li+$s?J0(CRmIfCMbzYxlx~8z8*L+lDl5Kv@{UGCE=iDpG?uQcjw%z zb?0mF7F7tS6dp7qYDNk5!-=qZ<3B!XbQ>xnI#l;b^X&sE5KD;B4g7L^lrl^vFu6^c z<8gT{6Z|6Cx7wi#Cx9`i!sMD}bg><|72IMqBRQAL{MchNw6;;H&(r*NzM8hFN_`)^ zD!F2gF-AR|MAk+%+OX-D)ztW9IJaaX`j@*%pB#nhJpR1u6yyKIGVw->5y7!t)Ft4a z6wjs7(1Iol>$p<7qa2cjyhYT<>_1v5r+9P;fN%Ren`wGAgVRtIfxL8mn&9|!;4`WF zGRzshBdo>=)#$qfDE3}kw__r_93Xjpe*9KNz#%})6K{v{=C+I}R@{nsnCU{{0 zMARTf3uW%XQK2G-F`kCxhR!~Di&h0g;yL{wK7pg6HJy7T`6*W(d>)`IB?eqz^q71f zw0XCzTU%3(T=v6zQ&Mu-TH}Jp>xn!4b|NDWkO6&s`>8Uf*6zSi6**TYnZ2z8>NPSd z7nR!$M((<|Hi~6cwpVNYI$ae`-#R*dC^x}qn8qw1%7=PY@v(A>X5UZkr4iXn{2AnPLuo|rxhy13}fU^0O&bO)`T+OQ;I2U#5R1ILf=Ql)aS}(Q;RGy;^P=@>5sQNXj zI7d`Kguqno_1LYQk;MebAL6R#%+iZIoi33dq}%>Lo;^147F*+#?c%1zN}<1e8AIB> zUjN8$9?CgZ?sjaq%$`IsmT!YQIx_7E&T#w;G)Dp*u^ZRsqWyP3Bv@LsP#2s@RVp8= zJPr#*{&OsP1!2VR+q3$K5t~!?$foKxl8go8MAzKaYA+-wwgPntj+O;jmg= z8Y8}mmWpmTu5j~r(>>>`$gVopO_I2ZvXpJry28TuL5(hC3Pcj3xN`~2*-`r`m9(bz zx~0eYIeR^oYFJy74tDgUC7CXNQurm>8#d$<_2`VcFE=`uwnhzPgQ-RSjIOTb&p>{G zJ24{w8wK#wgPj?j>I3N)chA|q`Yp+>cizEJFtwk9FfV4;E`hizRYx2vsx)k?adr#s zS9;jc{;D`~ZSq9$6%pU(g$d21FA0UQi{cw)#6&-Q)-w9+qCv#iD`hYPOl_6*cPpwi zR=iDaAQ=e05**8NBQ;5q)Wrr>!RJpx#_Xt1vW2YCq|IX1yUIqh_6q6dieRQxuk1UU zj}sCa3l0cMGTfwm`koK;1`A+Hku|jtu%mPP>wB&4I@x5I&2-i`lAea_y-X>92$J;F8$Z+;MFYarR1iphe z3Uv)r6S&#Yb!hmMxwPHSkpI|_RIwe_|b?eKd9@KaE?J6;2-Qn?*3s6wm{EU6YGI%0l$I$bcSI#Zpkzr$ zx*Yh`O^rBQl^6(rir#T+CKL;{aG|r1Pxw0h&`gxm_qa4L)a_iKm+He=pfqw|cvRQn z+!Dq(jpcM$uO?i7IJGBLQ{&;~RJ8KQ5EAgTPg3GwHI`nY>V1popiDXOlTKd@Sgm!H zI-)J(RSisO{hCj&n!v2pw?5?#w#Sn{Zq`UXrp>xg=ttV*%#1UsxD+Hfrad9z(^@nX zK@E4o8;=+7lf7~Ag152)FJbp`6l>uUDN+4<7(TsdiHt|<5?oDzz(7+D)@tAI+cuK9k-_A|~o zIY9~3c#!po>F9Q)jjJ&qxnoLUYl9fW<0|jMy1Z1Q&fL``06+AQ!kw0Gse(}-@qxAP zYD^O;a_%Dh?Jb!*Uk>!+Zi(C_1fXg~lj`h)y`5 zeoUSj?MV2sG^MKR2-m)2X15lb0xNCXzIOF<4|Qk!5nrxK&o{`C^4`(cKF!I#>wyxn zS1h)n5ykW&?|TkAW~_A1eSZxKyIp0sbF3J(nM2*+&V;V+xt?exmO`%cq`9#*&NtSy zfMufc)!DW^m&5@Lv0B?4Yelv)C1Bkw{L51zPEvrSc=Z zv^wLO361dYoQ54?f_4-CLYI@$e-B?@3D+W#U;y4Q zJa*tLcgZ8&XUKC1*VhCXj5UGK%h~^0qW>t}{|SEi^*6udlRPr;Gez*t9|H{8H&x9y z?}x93#(`);U6Z)oa;`LX)MaU6Qc4|qAG*t&(YX2}o~GP7$mUANpI`B!H=wZzCw|S^ z4AEQlg&^AdLa(WEuGUguLXA@nBqSP?YOAHd6q-I%?X{0AB&D9JIZg<8>G)fdx_i?e zC=Bp95}iIsDh_$%w$hlE#2C*ifg`qfEMm8UpZ%p3v|7Rfu7Dx>hf_b zCHGdUo);|eeTF-Wx9eYUo`bO3n51<$Idlw1?>>A@kMp{Jgu#@@)9~UP<~m*L{VVQo z_|uj2J&u;@Y46Oz!U%pW4@zezoUyqKelpIzXO46ZSJg-LJowtk30W74{~;l3TaU3@ z4H~`f(rDfCgZ!YR9`0}}yWj);;;3>NWU>9;P&v*t`kn};)1kIB6Fg-s`5)g`_y#ee?)YA{#F=9tI-^Uq<-#kF z^KICwcFn-J0Yv4dyy3bfpH?(W00?=-%6xk4?UVQ-OrflHDD(xq$vHvwHVR^biCpJ= zE_O&K6gC#O-*I!9X{y2U5}2y0`btqSY12p1)*~u#N7I`~G~L+lPRQUT!0tB|qZrUe zmWVY6SL~*@d1jaCmn2ayVM1JD)GRG4okz$0-pTI28 zKld4nV2aZRa#>N+@(>z}olIh;0Gh!9gsa-)rq(!9kA)pNy}N`i4jh5soPR}sCfkW* zJZsMnnE@}nmp(QtY{2@1<|g_uhB=gVou^NUuUF`Th666qB4Uq&@XAMpP{VrG7Z zD(`z5G5~KJi|f3FN$RmmZ(XyY+BHPOqqBO=*gV*5He+aRChC02C>yz_Ttzb=T0C5l zPic0SfJGR%kS}O4j4{WlO?J=+2cSA<`i~!7!Ns=4CovXqI1Y*=SkBW>?ir_Vr%b_Q zOJUv@g)oP6F{u9f!0Bvc2mce8tXlKhEw|HUOQe2T22B^(ae`Mr&_X)E^r%`B7G`z0E6oTZh9xD{p_t~($$Q?OSZTUK~s)!N|d=AS2+;ECqELYK&?<-_{7R5Wv zuch<&8NtsB#xwQ`9Kw!SqLU2EiB5`>2Xl9it!Inkb}eR~OdK@o6n^{=2lGG8d?fB{ zQA)x8)+Q;vjQ{*dgbP1YB;t1k{rTt8F5C@#kDv}+>F&sYS$Wr*2L|`_%d!i;^dndh zGQ6#~9?9NgO&3VW$|L)J!8nMau$t}AUIjDvs)_bsHUM{jsO zm4)E*<}PXkAKqiL5*{5}EoZndm}ENDP#BoEs2No9#dq3c(RyT)rB6H-JS7ul+4Vpn zVT#}fCnGvK8sq|r@07#%g{}n)b!Gael(|}M)feyBA==t%a!ajUNexf3vfdb_I$bSv zrjoQ+#%Db_ zg5@_ft@+3P9E$+aIOAhE_=N)ff$yNNIhr&JQpV1jEeKUTp?*KeD!l5v>2y;pLY*rE zf0U8za{RA3BJt*vn73=155D<4CU_kt<2AKP<*sHxuayb)bgFN5m6)o!XCMEESr;$W z${%#3>Yu9hgbDw!^31)qhxp^h-DYN9Gt0X!T`xnoxam;H=csDacfa za-xdAVH(Dh^b9I5$N+=^H&je->Gj_-yx%f;-8n(>3DmLxoGR?qEZlSO9!ICl)~*S_ z0+3_#Dke#$HUVqqy_}(a%8}90o*lA9%PlX4c7|q722SI0WtM<+oFN~N=hZL;SXq0P zq8#;nZvXROT;;k%=$cw>Ii_>>Zd}hP8?vD;)s-iv7Dp zj6eDq2sUtB7~P`pxr|s%bkxy7u!hO(zQ@T1J(zahV!o0V?8)$~PVZ1IKIG{WvZcF3 z-&Tl1`cAp1rd0(U{MMGVAE0W({MfV2dl2Hu)|#j8aglH7zjP$roh-WICH#tmKG{iz zk+c(05T>U574F!&(w9kEkJj$TAB~VZBrnO!lt?dTZ0iU`-1t}(b+98yw^V1XhZ<;i zK?K|k1iVJSgX-*JLs++!{SIGZt%KLtYk}_pZO-Ao`?@@_D~5r{BVPBn$&gm4Jq4le zb!nymQg4fkNln1#IcaY708)%os?VJ7(AZiS+0mj&E|5D!As1U1NixVoMaGCuY(1y7 zS;Pn-n|oHSK`#N5$%dq;AzOAOk1EOiMzKFGfh6m!?I{XHK{hyFm#QN>c_SwnCU-gi~17 zb+E=-Krc1p3Ur{?yv>A-BOt&?!!6lDedL^u1$Zc4r}$Pb`tSxoV#E;4jW+_#f{=b1 z=+&MsX1l8}`!bj3hv6qd)r8md^xfUN{OkkyM)?fEJhaht{fVjC4KG@h9+-Xb1lbQ~ z%W9&?qE(5|Wabli&lXU<+3Wv$e$LK%WhWywFc%TvHHtr{sS}RSYSxL>piOxdpTafv z@p~TS64$KJhl%Q{AJ+AuJ`8%Pi*@2por$)R3t*M)HDrVhWd&hZh5wc|uz{=RDWk?@!fW?bam>P0);p z?;ot)#)xSzGC2tYB|QohDpX_s7?rE_`RFALvo|%8D#nUmUUOWb)NSJJ%J3b1YxCxU?02_W&z+ZnFhIdrkvt6k7^}|v z4z%gFSb26xo#oQ)GWLQa3W~BVjkYgJZ=Dd4tOz-C2d6UAAvgC5^4<`-PBN zLkCwEKWlqB(LrbF9HoiAw0+aJiSt@3IiT0^lD9Hrfin%mi!4>$0)(4YNyK{AE`)X$ zF}!QlndedM*wmR)g?z8$_}**tmHi6@wU|2@HP_bD#j{iR%M~iQ+Ls_Ix!fp(U|-)- z)9WkohQ3|JDQ`2i{SkyUHZkO3e3YVLc~?*L{+vIEE+_t9UiVJ^jbCl%)9l`6z%pc-P;BoAJ3xD|)E8h&gIPh1^($*d zmM^$9oyuTRY#MgOwhNj&a)(n2?KvnH=As_{yq!mFRnVFRVa2E&TYf}smQ*`P=9dV) z4Rp(pmOI@J(=cEGkJpfIhOrcl!(5Tv^cX0nzPl~Eq`IWybN5!i%Yi*a7>2aH?j-2- zc@*$HG5_p*I*qxreR@uD%lr6V#NA0%&A>8i*HnG5bE(^)czWHJ+skS?Hbo}d(!#?4 zd962kpbta^)ee70fL{GXrf7s#H2>IREpdR#XlL56&pXI z?vaYIWyCbE4nsIF%DP-%)^t}Lb7UF5E?V6?{Yj(Gx+xgtvaD#^2VHC50fcK@FvH#a z)?94L)fuNTDw?$j-s9u=^`RyB&Hbi02rKl?v85VE!?Z{76hkuj!4}gSMvf$51ss{} z%{;2~*v-aJ4yaVm&z^g;esL6HsrfGznT`6c&wTe+DeoHKFwLrEf?5rj)qj`=zYnHSMy&bINPi{n%tRJ1v z_($U%SXVG_b3oU2(w|4%CGc|hV(7d5T_+)nFicrlv~hH^IJIzDC%Gz-|IS_+MYAT! zt6{DorPHdOH3*f|EA5w7xGA?1DbSv+Z>Z}L9KLaItPr%{2Y4D={F;3wCq-oycSvKq zF!yPMM%PFDS;@gC)~oU7X5ep$Fve1s865wVd>lov+V;#%&!VnuK#jU3~e&Y+Hk$$G6_feAs%5NeeWmUL^u z9R6o1Iq_ekjv0f8*1{Ca{?oQJo-53G4u6?}#bY1gvmKE;^jdY*@8lBLH6k3>Vps8h z_v`VuqoOgDpkD#lG6I*p7p0Wsr~C|$6UhfYto=Ehf$$-~zd{|~x>xFKUtI!Bs&;ta zljhQLDWnS8w9?z=nVvCAbU&eL&x5BaHAwuGEwo00axmHCMgs2F3pM{4eE8rH!S(b0 zS5+ZM#rK4w?et$vHzvB|6qTPgOBO?IHAxOq*GMBJUQ0`Kl=Fi!1rYFZ{PBUUsQPnG ztS*|mhnfw`^cHr@iR*oi&^dLi!||6&5j4q0hSz|MWPgl_?#9xM?2c(oY4R46Us9`} zF0eBa7qhmz@4Y1MXUP00?rpw-R3xf<*T{ZFrEflM_b=6=ni$7+L~J4SQdqDOLO0{5K+H$x4AxMFmC^G`sodphI7Zn<-m2v*xRRYUfq{6fluxz z!U`ErmKX>&@zES^uT|SOypH#npqOA`o@fhs=wietr%lRZFfH6p2*q0zk{de6Pu6Z!FbT>K4D`$nv|N%88N z?kvVmuJP_*i86v4dTgcVH!%^K7zcE8cMdmdt9cc_l6JEdM%%?!H@XLd?gm{8(2HNR zUCtWZO1Jc+v0hVgk9fu(`xxE8uA$8yGIC8!pDkrZvx*P>dRURmDzJ|X^;Wta(&Q4S zH?cY;IGx`ZDOm$pI$M0z+fgQcrg($v`ppP*L*WL`t{i^a8X~rEO0wV-8`-7`oQ?C^~-qJ4cRYb ztQE)Ke`zjZm8++V=$5R}3}bY1DM_as3BwpR07Ki!0Fy#p0$2v+PzLSdPt7?uFu~=z z)sJkSI=9AhYAp;!`S9AG26>5x^Rgt~It)LSRscB}P#_}*;u=>FAwBmeuu72i;a4Zr zS9^$!5v(U*rJ@_k6_0fVPnP|<(YQSAL%Yxh8kMsowsgcp!P_c^repaB%I1PKP@?V} zftiMDq2`TrkDyOB4^H+t%PA-B%xpGM|FI_IWUKZFtPg4Z4rhdhSZEcoQY@Qn-Ex2}YVVVY~Ol4wdUHB?Pn z!8VR}=5gvbtM|C+$*qqsCW3S&QkE^i5;fJE*OD3z$Q*j(wrGZrwczO4Q`jty2tybe zi-5eupI)Q4ghuo%(|q`t;=4kGk5b?so;aTPP{EYCun|aiD zMVYbJFqW%J&Zib=ri5Vp3;@POR9m<_4|pZi;$A3NWF^1T(6s-t(5@kry>^suRhEVX zwuItZ*TdhVdx^?;l?NZ^lnmjAMo!+**|-E=Zp{Rxz2*F2{aR$jfqCTcO7mR{uCL-% z{5F@s$CSBw4N5aq7n?TOuSOH2@25!w2exHWF#oy?j@BqY{W zAd@-*y@QiUx(9E8rz2({??1LouARI1$!J2@ZL`EbYrHc3`XS+(e{+rsCL-{h59(uq ziL7-$%}{JJj0i>Yr&|X!(zrGUtf}n6Z#~gY4BW?AUjlE~UEqPQ>a5_#PsbaA;)o|a z#aB}&Zng8cB=@$f9Zd1NEG!Vkeodis$-EKum&`1iB4gGGet{R9d)_}ujNSh+cp=^aa?c^&fn_ZW#G}T6 zxdaAs*iq*39TcdE6{D%V=?*ZYL}E6wSot&qIV!q-FE%Kq%K7zR&YiXEW~TI6!CHHq zCB(cT-5240WsUH;pP#(d{Ve$#CUO0#yI|D}{8l1~u47`cepHsx_l?M&$G@rW<2y<3!2*%LO11i%?wOG@Z3L=IlAzx#WsAd ze%`g2x%r{nCE#qDXzknD@{ZnTFoBm=_6C{H?Tb3q+ekwajO2RXsnPDl&Y+8*{+k0X zl{u~a^3Gfc-@JyaK!^noQ{Yzfor=3b^A^+}u_{PzD(L-l^QjIMx2D&-vnvRzi0h%D z0ULdgt)`$eqr30CUNRzoMehZl%kN|~AtDbWB}UTU&#AXNrZL@}IM{$s_72rBK|a{% z3(WMZ_cQy8G<)Syt4x91N6qhIeAaRkt!5)6nLi;2ch(TUmXDNbC5tB{b4Cj11Kl?% z$_u=}ct^7*WhJYMb|ID>vsnW(L9N6T&0s;G=tjG1Lo)|gmvo(#-0i(MnqDst#&ZuMb*AQlV@8Y1U~Pw_xRPa;^?#Sg16R! zb-K0cD7zjI8I(w)74;^nA=Q_naE|W-p72KEk-~d^;lB09b>4g zVVo5wK8TCo{<18%)Vt&mqTsL05H^wseTu9Ly@`XZy`_5LY+eUK;QcUny6oWRftlGH zX>>FG!posLCT5kryhAal)^&!hBZj2Gd=}kxt8H-@?9709xkTfyHBngbL)b0y64-M; zm)m)LcNWL2%bTSCVccqRUs23iA-ANmy122gVu&~ArkVEDy259@9I>ZTTv$1jUGFHN z)HP`L*I_VN)!b~9xXx-6tjfJp(ws@E%bTJsrZuF#Uiy3eRF{;5bwYEmZg#e!ui8u2 zlbq%|dDmWDu+ft6%(?mCbO}gg?;=b0qysIHDaHT6GdrQ~7|DsL5yA%(df=om3Di^w zEs8UC&O%$D-h*;N_4WNN-I8=|@uOj})Crx9dg(#=1rbEoM`5Ck6~d5-%&}g>xL-*< zNz@omg3|5h#WPugdx*Y*M=kZ_*ISbA&pEr8X&oKuZ0EK&q{CP*ihm@<<78XMPeQ3~ zyBAqM+F{z-{t>4uMQHIz*Y%r1Hrv@CM_q3h#f`IPhQ-kAmA!oYf%vuMxBfY7DBGA~ zS2YDS87`p-{-9vD)yD_TL3Tnett}702Q>zh0`EP!9;BbSLFrHRF>wVj3sgzjGAjxc z*-%S@zg{|ZwNb@jj|H}$(p!iKlm){xGrQqm4u$${$JcW@KjTsR&q z0cxgveL;$to8~*4X2GJaoN^JvpZ-Z=K;$@!fbR?qY>lxxSj-6S&RAOxS6R`FTzc)5YYXBYJVF zbrUe@O1I3>>G)+1bV;e)_-+n$EJDsKs++huS%*$DaP8(zr)UR9}bm${_$cpuUa2YdOD^a>8~Qd~JoJmr?z06(;ofbovQ@xxkp=M{8x zUv2y{#n&H)-0o)nvx*s3l8u37w{UsfL(%tNi1_Y8K5YBPejA*#NXoIK9|`0PQ-fdR z<75p`^hmX8w9I6Wa&z3bN$_UoEio;NkV(*McAb@u)>L+=k?66&gecX!cUOtYdulOsnG%T5bE&*`N$wgj%KxiX}YeTT;` zGfb=cp-Ov~i6^au5B;k7jtyhprO*v~5yUg`(7%^H`J!Upv%-wK5;BG3`wAmK)%LW3 zqB=BIA=ke9M_5fl8rwfg{4lWXe6stjd;S~%B4p6h1TX?&>vyJu4wc1kR;UP zs^9DYg}X?jzDZ+aOH+fHzN?U8HJf^5@fqJ>N&qvG)Muij+2*1Y`w(dt{`_0NjrONm zt0{ytLT#WJ<8E9xUG(u>&Ojh{TdBFRiQK4C?nt!!FqOoYn@x=yYlP`R?e^vX)4tSK z?5u38T_@%&cb|ao^zUpj<97gdw>Jx_cq*zp23sPI8$u=fk3mr@^`*TOocwFJ=GyHx z?nnsk`a&R$%X5LTTai^M7LA#UW1tks*5ZA=v#Rv(#4d4BVl835aGFpzJ7%cQC*{!I zT4ERoK6?_4n!uFcHfzCEkDD8X1q% zAtx_1bplqjq}#E|Zb~$j*Rm&R(|A@bkU?Plb{zn(`tg`&U~Mkm;v_IDt1D5RC#LLV3@tG6%Kuilw#<;C`CY;ARuB)upl4;g3_Bx2N97LBoK=9CLkanMVd;L8hYqOM5OnU z&`ThpgwW!h|895hea|`f?&sY7;e6mp=9$IJEGA6e_t%y_rEms_@+;Jz*4K9Z$h@g4 zZ+eZxI;`YVJdH#Pogk~y0SIVnzVw0q@o-zi(QLR|BNE6!7kTtJl(%eA;7=f;7{#Mm`-YOq$K8u1raf zI554%pAgDxEojRO4Ri^ue%&zc2J`u20b2O-f$GpQ-sP<@zA3=VcGpZ~b9udU{pM5+ zm!KlSPCLM4M)=*y&U0;cpp>-3FD%eo+!!NE!Pm`Y?u{rM7t7s{-(dHBAUx#WEgbv( z_+j`9#Nkrn;x3iS{j|sQw_j5@wOLjrx5}_MKAvbj7_FYOX=nn<>54e#s+-Qr)~LFq z=KMoZqr7+p%3gza%8=UwK?dkxq{DU)aATi6BiMc?VyCo*-P2|qd3*|QhE;*f);5_} z(*z`^&kMf(@@!&x>yqb|QYjya>lk`sfENu%Fyz92YRQn9=7NuG$Z2_ICYr*@Z$8x9 zTLl_p>~l0N1tcA(@pJs+s@)Tb=&>t+?4`PcVKQ}d- zGdI805$7{vkaX^m;zVHU;#T)@79({iu@o9$pU-%9*>NO7 zG?}S9df5dYkbj`*PBidH3?C{G@mQp$#&qV+)=kYDBB?{#sD}C^?3_+@{L>QrMEeG< z;;@-a53fTNE9Kl500;ZCkl`O?8vjtOa`F8Yu>XI2h5eOB`-j8)1LXE!obEpu_h$?{ zp$g=^h~#Ku>0QVtnF~b72Vt#mnyPaE(Ai6dZyK=`F1v0^??_Vz-fQNb!KL;YqJx*l z8g6*8?MJt6MZh^BFS8QAf18gAgwc{HZ=a?$iINr;z_Bk+p7}cDz@e65lV0JQ7;X!X zn{9dH4q(=A>t|PUJ-6K)tq>;Rv_e@=c{~uiU*TTFbo^}Eyg5s&A2)$7aW?2eq@bpa z!UQTAr@V=t9O!dqn`w-K*_KZUM|&nS$jjlX!x~pU^9p@D97HjE zL~ppHPYgKLP@PAIg=5Y&*h9tp!Ql;&JzHM(Iy@FIqVjl`e6PLmvB3N4=Mz@&2~%7Q zXOc@c%oa=sbXuw;B(@jEWU+pb>N^AE%2=5?v5$S5zRgsrAIDU6vv+EZX}Op3W(n`N zXhdVKzt7BEmrfwJU%eFu;E1{GA346sy{cp5a5={@VOrHKQ&8Q?fl^sBXep6O@xnB= zQ6oiH^Ts5rTZU{*?OunU@(C2L6VbpiKjQ=hA&}Xfpdv`dlWTatNVfR#yHdGhNuGwk z%qIF>g~FtMfUkIm80bHmxYKpfH+`Wr#FBRlZQDcPEj79B67RCs*_ z3U#lEak{v%Ews*kr$-kx$DfZr3^ny&uBHE3ZZj-4NCHR#CAZvA|casIK= z3~yX~R4wQCkFKU3J6@Zb7)yS*5YcVmM2NuB<~hsUIJ8xzgdN0P-u;BCR1R*Ny7HX! zjOhE`rL2dNK7K(P@}RnqwYbAalyYL-rM*&=0JhR_p3nikbmH)hAd4s0FVybcQrwwp zZ`s>$ubpzq50^Kx-igjFmq}``c$ffPd3)Jd0&$)o+F^MX@d!_>J-)p*D^`W%CdFC~ z?)9c19-@vs_iGyBJA?w z%}^17bmT%Tw^>`3y+gheho|T0c1V)XL1p+`Nl9FLLQkzuO4Xx%wjCK96M<&oauC6% zvu^wCz6;xoy#;mMuVR+=B#xd7;h4(kO9@Z3th62UwDcE)-wyTgRdu!}>YGf&ef0Mm zx7Lk2mDqWV6^(8ZT)?W~IIOTZ?J+$(M@&&ApTzyZwFFJ)>mL5`qdts?ltr{@lB;~g z(B;J)V^EtHlCvweiHkhQ&q+wHuE*A=3mFn5AI&*MBVWvN486#8uhKfJCCJqtEx+@P zX;vXe{&l=ft?C3F$nV3TwPyi$RY{&(zM1iwTckv-kadV%Y#-ANj9KS{&`Iy9ci=(v zH7JL}tYj^TV*w|ypw@wAluiT?SboRC0#cy&v+CiO@@cQ)PlI1d=Ae#+RdaV1js+!K z&U8jMBwrNx;+`=A$k#Um90+&X=09GyWX&61_AV{ms;t@X+*Q!@<<>CXTU9uJj(+U? zgG0s=S z{rb-xD>0jVNL8H-7PgOvRKto)on^`CK`_bvOo*cZco zWTj4YgC#}_+*ZdCP?DfEjL^r@@iDo~iezrOYM1xPe`O)#@^-T9F|)LiF-6@NPe$-3 zvC^M9^oYkK+`={wKnPUyp(9ux4&xNSEVn(dN-v~xGu9a{Xx^*RbrGy8*ASfoRI=*L z(8uSJWUg}VKReAOi<2^mE_if%wCtYqO?`p~Dnu$S!M)$kqu3(SG%exfipxYRV)R+~ z>|X8`$N03(bCdnB?MrLd`o$s+&b#A?*m9E#uQEN7k*w#a>4RFZu{nd zlbLcZS_+qhEs&UqA>md&jPcnnao32SHwPBB8BcUbOqfz0Qk~J|k!`6Imxi#Dcga_~ z@x8q7b{G43E@@zUn7#vDAV7Ay0b(D@-+v)i#jlFnXHhk0(d!MUFieUM>#Cen;>_da zjRD>XK#cvqBSCvWCI}uSSxLeF4b@+e?VGUEE{;#ktlLZ;Eq`yxQ%p*jsf?H3X<>#$3ndBH zv+N}T-~m4;NdS*(nGA_6AR!+xO_MxZ7c_@*lN>%f`IvrXHy)t>xgsYgy>bHqs-5a^ zg({xLlR=1kgy{Ylgy|=Rj^%8;=9jyCON;EvZoWnrK+x!H ze+Y-g>4&DXq%`bvg!$;6tYWpa)V4LuzD84HYqOah<;5<3u`*+M`bwBx5_|krkO3!W zF1kFk`5q~Lj_vUsn-PIW-1V)fWT($fxo4@59kQQUk9`F11%XrEUD)(j`vnkWDJPdm zKCbxs=20?pVxQ#~w+P7x>lm&02je43k^1KIo~dnP@D`M#evr`9#WV z9?fa*teB206%AjBp{uVqa5hm?(=3&nwVzh5wEs>?O`jFqUHWM?3(SSjY!(RdqYqLG zRS$jiVU;8KI#JHjF*W$4tlu-5wQ2`sYn^%h<`Oqd|7H%J26H`ftcxG? z3)<#;#!S^Ve|_D_8G5rC{5sV)cd)TB;%Bpbp)>EZ?&YR%;16}J9f+xGfpOTI#D;n_ zGCbtY|Ax3N`L6#~W2<8>_3;Z1#_j~upKK~%x_lbSKqru2_guvhi4y?}RZW$6_La}x zLJj#Q^~>-Cq{8>Kpd#`&sEYG9$kTTB-~;*LCblU)+g~X&6nOyx#;3I^Hc^qhKC9)e z8HC(dw~}n{xt1c5r1rXBxV)YtUd`6)xpDaAddI(uzp3fx2dab6_HFuJi~!HXX?dq% zg!89aE}_&})g-P@NtZPQlf>C9ZV1`9Kc$T`PiLjrU0T_!z$+fKyg~Z^jFV(HF&pDh z+%>hdFr7PJ>g8KvfR3=KkK00_CXJV@6c?6XmW=6>r0|(0c=rrYBmOjOP`JE$s3Gh* zBd)sqDKV~-;2baeVjjZ7$@%f6*5}l{bur47HMGrKUg+tgJ* zOumvw<5%=TIr}V3YMN$$nt`D0-pSr}zqzvB=xU zN3*aPI`O%|NsQfTIP!aB-+OBFMc$9|GV&Dr`fR)Yr$tRvG-Dz)Bl)Or_#HqZ$&}`r|@NV;vWGn33qrCUrzL`^7FXspLCF+%& z4|{t&oSlWwR|2N4z?^uH;1_rrvEeimB7m(q5x_FMR)48pc6_hb#!DnUeAr)66A6Ep z<|A4=4au#6_gX)FEe3Eu5-YJB35@;!b3qdHmYd0lMc3ev7ROYoR` z_Rt7f!F8`*{iZi7#hwV8#q!o@=cwk?gx^;)sueDv=z%FsNxKITVM>_E3GBHoXzy<2|3EXCxXtoExWJu-ZmNI}fN99wB&0 zX$5_GB;_r#J#KVulIVPeJf$se-63kzltESXH0pEWyDd;+H*no-Y&&y7FlU*Ggf0`$ z;^I}Zc+8Z}KxNX{O8v0&mi&gdy!-KFdL_!WJy}smfUrveuR}~nct#)F`Sk(ea~{7z zYydBjqxB9gquF?j9F{Udxp)sVl6%|C^OZ zS5?2UseDLs36Rm|pirp$@y0e!!@`0je{@mktMrKai=7!^TOI(i7gS;Xnb=&Y_@F== z)1qP#>Rp=1m8o;rwsu+=b{1~*vrUZy0is`XI0V*B`! z3=?G<`|8`zoRm`3^-7hTBbwh`)qbt&^#)p&3lDuzJ?2jL#ihta$hde*YQ{RkXxhe{ zPs2b@nbB2+`^ledB7ZTi;9->HzB$?k7J^#)99PcZi&BIp*4*@1ze*zVrknQ*zYb@s z#k+15KbgEen@=~F#@(AsKkjdnMCab_*Ee$CCqT0<4n7@xroI$N7c036RO26Y>@Uyo zrA;^F^K^y4g0ChK_H7KofpvMyJr+@Q0|74WDwKSqtm+8;aRk_Y zyMKf1t-UX^58X_A@|2CzIiZij^C+`bAduUx6-~c>B$+106wUdd*d&I+7JH z{B639g=(nlrsP{8>So!uH$+#_KrNI)7FdJQkSq%|F#;sD)c#{dP5bAyF-k@HdN&=O zePj};zu#mUY`@qgE~t2J|GOCFcQP0uSwYgkh(m6%?M>#5EbT9U_4QE}cD}WA(g)cR z3ZshlQe@)D4v6ndf)<53eccoML${;nU;!otVRT~IJz=!&+X8FcQ>3j0-I2LRHP_$< zzGv}=$?<_TW#WPXMEitOs6=!<;ljxvNdzMk*2GAvz@*b)M&k(JJBs{51ga(p6hW$| z_E+wp^yS73(MovFx405%;Sf`qGVHR_l+Em5Nzi_pJLUKFled>=)li9vZUM}aUq9eV#Zm1_5j#wpd9 z;>IkD&vqBSIKP)vfjzYYx_Cq9c`y^;+B^~nYPeLQ2F;XwBUx-y>bOnYUH5V9-Ps5} zXkVtIcfCj|MzdNdnjW3)bKmX$$VLQcw=|Rt!Mk*3QX^ZnvQ$NW#(KGW*+h2Jo^R@T zMI7Rb2WMP{Pgz+xm2e_*4cqiKkgP~MbO?+IqMwC;44>$hItnmu&E2l7E6x0F$~5C;f!0Aqf^=dCO>Q)2nHiZJK|&0E=U z+$J`~%KM(5Q`buaYDBB)Akdf00pIb#ew3=tU*J4?1kYIZpi;%pY`CDwb&g$2fMO-~ z$lnM=q`qC6RqAHS4t}_)a8Nz6_vaJCFmqs1tO2?AG|OX~c0*uNd?5|SNXk(+XJ4P5 zGF8pd$)8Yr<}55OopYLMGP9JzG^@e37_FlIT&EIkV@dCn!A{bZf3uu+IYL3o%WUMu z-deJCQj$qbo~wrwOQ%x!$yc^inL}$+J07Q1T;ZAuh@KW?@+a}hzr}Y5;OC!Tys9Ur zH`QQMZ}am1#VeG#)H9*OKRiMmGr|Q8^kDo=xB+2wsHN#+o=2Ui|~gZ zyvoZpfRU?~VA&>f4ldDJcOhp*)7j-y@0ZEB$(I_~v8t(b=q=O%OXpxWv$!l@!9#qa z+Q%yzoG#;}`4D!`a5nl-H#U&F{tCLBa|lf3tJFZ1ZV*mu8bJWtdg7~VH9pf(b?BSyK@KR(r7PKuo0Xx(@} zSQY(PD+XR_J1iy5K005TX5;!AvRr**GOpXG_4@!#oUYlaMUEuLaDe`cO`QCOdXc1{ z`o-WoYP^n2Oosji1)xVaL7+mF`M{fxXjZ^&{gY{((95S;B`18?Z5D?k{+L+_G@hK6 z--jtyWf&XBYJH1#US_vlw_4WVE<(yN6wrw~79QUOo_dm`9KxMBH9~*5q8~bbl;h-v!!lg)fuj=XEB3H<%E-NMTU{B)X$rJrSQlugJiNKVf1SO zNk^MN<#)OAm-MrMhWhby@#nNw`JIe!E3tH3XkMSE6px~!AA;G!7*VnEI*jm@t~wyo z-i3BOIdwEVf3^(-m);t*AHs3yVoIJ@YAl)>eZM?1wJaZeJBuDRxzY*9C$6`FY0cnc z^IPaoT~hrM?tMJ>hvmJuZJS1Agmt3_?~#$ylEixw9?_h<5;U${ge>7m!> zPOilqsG#Zx=Tt&<1}>M^CRqZ`8^^0L%TLf6RLZYnZVTRv7b0*voHI1@{px_1TR!fv zpeKa3gPCUGG21N$2bs3V_Z0EDs4=VW$B0PUfECr37My|p1tooBoP_Og%Q9zFtOtx| zZ4w!=EuTq;(5gk%6Yj_HC+!lp zG1oiO!EK|-WG=-zV&k)X2Pk(uPZE0Aea)Pic(^asOPxE4$&FW{myRi;k~{ms8)3eJ z3m_=Y5x*qQA2tqH$&$(iz^a(ovT23QBkQDAw*p{xq3i?QXAju(g?2tCnf`c1({!S7 znzX+iK3TODTT0q~oX}#D@%35Zv_13G)I+OeZ8)GIAKdqkr!$D3y}_pl2|#f!s=T^t zN|dX1VYG0_=`eJfPgm=(#HX08!mLr!lbIQ)U;`lYDrutRomPdzdx1C`re`3UiPXf7 zrRp?Sx4dTQEurX1PR+2lB{biIyIapfnALhM=}%-xg~uiU<)aoMA14r8+0V9WA)k4> zwobJ+S?Al3P{hrU6v4Rc_#Le9JGH=HLe-W_w}bXATaBP%x97jiEiiO6b~Z|lESA&h z>qm(3FZC5kRKDv}Qk1w(P}HeH>=Fcscj^eSZF5|J`ngGQ1frO6gCIu=eVj2#=WU`+ z+gjleY{Pjh(%LHYA-ep-Oro*eInWl`8Xd_mL+Z!pl2Dx?Ki3vUJm~1O9&1IYOYS>K z+=y~@WFJsB&xv{UnbPO0kl1Fh(aFg1HS)+Tgj9wtGzIJ?LE0e;M&>qq{a{$A0-wkh zw8d3ex8rQFmpG~>URY%)47+6d#y{P!1@F>^4r}5Ya+kVpNC-0P%9-vSy6cobIbk|u z{NdpvW6lI*0xu1HG!y*;e?POJB@>#>TC5Y2d(}dO(ACao+D`PlkV@WY8yzDOTQ!~1 z7U4;zg<-8#n5qc1h;g7xmHsouHP4Ks zrj@c%70-9=Nu|c)W5>tK$lOB7_=Z>a6+fweGrDF_+S)%JuR{-~G$LscVmo#b>iF$& zX2UTS{UIE#)WfOA*l1>UW}?#OqqF{#XrX8(eI{Ktm3?rKK)G7$T!1Cpi7COQ1^nf- zqT`p*lorFx>C*HgH7lO`*|M&XTA6y0u<5I;d|9-hxzqPh5qQ@Ecyn$Zfbkidx=!nh z95xgVhjQY1?kZQ@Up}{F7bhh0x?t1j!e@m&5pS0ko!~l`7Fe*dKx?$%meYp$1xhNn zX=-LA>HehiJ1fgwZ0k?{*3;{;RJpBbs;dY&WP#ts;$Vfb0zjTF(;s4NS;Rv!*x3|k zxpr4KMseo)grf5?XFB(kSIY9C|JB&@~Yz(}1xH}+C36o$Fz!iIc$ zD0`@8Xdm?gXkYtNh_5%t&N84ke&UY(9(3VlHly=M4}3JuHQQ5@KIN@)D-Z)PQ`S=s z(#g-hG-!!&TutBk5lJO`SN7aM`-c`!&!-n!kuerhK@0cu@ShM|?gZ0St-`f~Gwahj zUo`kyO?#+bv7cczh;xRF`2y0T}E`zU)dV;_~* z8$>6C6%i%-HQtNnyKgq+tmG4l%!igCldlm5<)k8M+w1HmkW}TVkDQY?q=jmB>3nhX zoizF9724pYMC4GzB|Leqx$ZK$aPn4O##aaG{mNd}I6m$9>6CU6i?gXTUGZB*tkzca z!xYPN3`qOT@JtpQ!Z45IqJ|&&S&ZS?a5Alr^mSbxg!xir*lP`%axriHa^A(--wAeq zIz8`my%`)11pjpkyiElV2qp(z`$4BO#<=-FH#eS1?~-bixR9`O^)e`-p!`e$Mn|yW zjlhJWmR?(@)B?Z>&pASsD~9&c^{_u@Z$Vk|jWi?_8a&Hx-jAOVX(yPO#m<&1(N(H4 z>Xgn0(it(2jw(cmy%z+Wg9TAnzHEXxDF^*8|B00Pcew}u1C__WQkMR8>|cFXf8GA? zzWz^;ivM@#`1im6e-;1yi}&f@qx|{T)IxthzkhxYkgNp1DVKGmkS4JP@V8v4QU&QB zn?Zkr5(S(>=XMI8_<*|~QCn8EZ6E6Xc|`3}?`h}B7qN|l_px6(K#%`n^gj;$KMwX! zyLEddU)>-4{_0C!_#WuBU!eb`zdKkFnjW6hHY<9h3DPBS3|JzCP#}7`-+?v0jPD#Q zxn)K3>ZD*nTl=K&xs6h1PqN=1M^c-_9Gn!4uu-%9{U$4g;UA}c;#(qs{9df9@}KW4 z+~EEhhnbP~b+fDs>HI!GpI~OD+`($!Xgm|9ue+{gQdw8kR26a5c`x<+JIL$%Epc%n z`iY@bOuu5pDE@hZ-GAI7o$9Hh%V0xM`V*2x!aPdi62heu`lIh_^ZTuvsma#seJC4 zvQMuIYs&(XC~tLukXN7_Buawb3I-VoWNxgqb2lZQO`rb?vH2v`l(kd5DeftCNBg`K z^9qC>M)}dz5{_&EpXg(w?=GxF@0s5{44d`N!qskQzpJ%x}Eif!4h zO#roXa$SHvdEVlib8^e)mgfrxK7F+f9GMPND+@omCn~lUJ`zjOu=xyu3uyb}xhXK# z1;;_C!0gZY;}E*}rfR$_htHXmy_pVQIme}kbYCyK0+Lb@s0J3}`cuNuA1Ozw@&l%= zyFXf(6(rm7XTJ1^j?hjzl<1`C^b^`{MyD+m6>Z!Av>r@Q4utBCjK>W4 zVa81@Q_W$VuGO(eY?`oa4^J2OwJSZmWe}esKP-r*3k^csgM*>$BxBd(M}EDOM)>3q z2v>oL!|XM@Me&=1R2QPh)D^m^%W>1D8fQ?c^fz^fm6cy!={@ek=&+Hjx-59h3CO_h z`4RW@`v8 zv~##luQv&@|13A1oKoYhkZbRuW9R;rAREUlJL>LR};HEm0QBBk^Cz=1MEn4N8Fv)35H!j z+Q~5Z4OoJk1a*LfOlhY~#TD%s{au{7+>iEvm8~#;^F>dM03l9=(`WF~)+2%F2H7ug zSo=4+bl4MA{3urFL|47u$k+GQ#^Dx8^*4y>2cZ~^dv-kl;`L&CKJ!9@NsyzBEB_}a zv`2CIb?@_L+UVB8HBkXT{HXTx+V^iaEG{R>m6p3N;kYZ2K3ZzY6YKSR=6mZ%Mfgzb zM2L*Q9k_|#fHw5!P4bo@8k%7#bqhRa89cd+!XiU})xJc-b9Ov;HHpC`piL6ZFqqUJcQfVlYqna2|=_wmxPrEo_iQyYe^TY=|m>h|M$Cm*Ld z=~)S+%w`Wcj#ha2&W3pRqq92ecAFIhyZNqIKRy$<1V&gU=la`XsUqoFEUX)Q>xj;FG2s&I&QxK^&m-ug(`$2=DL?$FC$34&|mHip~EBr zE8stWj7%$#qb^8zguG>(g!)G7IawGoK03TqQ(i{GHP6NIQ4TI-f;ETxj zhZY)>6vE&7Kt9x^K#ZhU#w8cJ$qn{xcy-cP{oRv%x?1|Ha_AZ7tvnT)8_mT$rD6Qk zWliSN2gcv*K zg4Pvx7T||>yL*Ja50g1V)HzKr#~qimEE6!wRi$-TDs)RXJP5Vb`J-C`HedWs$n@Sk1^rnBFjlR?WAv zphxwAr&<+LEw7DP`j{yyx9;SGVsfwYsj)4s9Yd!ljg6-^&EYsLad2T^) z`F$R!C*HNi$~!r|A)ZN@9{a0D-n^Ey`0|*aoQHjs6F#U0p6A@wWm^Ec)D2-(H>0F`ewcqRP;A=n7e445O@>yuJ`OPCqIW z)kL+{Bsn5o^<1`a?S2cUS2IpKd*xb8QyNmyl@5tk|5pfODUc zp@wG=d!%0ABwzMV;8IF*%AXI!?Eu|x0?(#-p7NECOnVAqiwU8XQ6DWbwyI4eHohO> z4naN_?Rj6$Z6GR?o%zOL`nwM$VSskQ;vP1=R--) zLt%f+`4)(6eLo%i^FxB)$H}B*z8~r_R@U{Xq)`K6k!ZPZtt+8SWT*GP74 z+!>#Ia?I6e&81dR9l>d4DlTd6X(htZ88(kwbE2Fv2ZUI4k}-a83gZ{`$_sG zLuY%qGcN0U@D8qcNP-?=U4S=Am}6^6#q4|;L_Ya4T?wSn^QZc*SeRSNc`&!Ph;5)S zV}SNKLaa0y8umx|e5NqDr0UBFEy!rCN)_!=znN%m=D{hGe5(i*YonF zW|zy$@+@1~YeHAV$Qwe^x1IzXXhYn{Tx*>D^mV#B!YNRn zHi&Me;7x zisEjApK7Hpr*pAYv?5iA+|CPzkwo<&a3KKeLs39-Jetz268@4p`Xm(+ljClaEHvn^_|L7~U$_6Wpil89 z@1HJIiLg?fl#qYH@D6jEPK4bBmk6?=B@kNnGTjY3W3X#fQ_Wr{H6P=&mg=o9GDTJjYjoCPH5hJaB+rvpur!~?5+NNgNa0*>_9eo_#UDDfMV{TVxV!VU*?#)*5# zc0hShW_ij6WB(1BuV|Y)DJ|W+5CI~?U|0qqu)35Skr|1^8)LS~hCt{)qYJqMx%cG6 z6j(a6AB@~-LVr^hqa>aD28CS*Dh2e>6+bE9<6swxKD1Q(cV77ZF|c|B6)^2UBLuK! zMc`wE7(uiZ9h7l#6ru`_@X#OG799!By)6RJIyUOgn^}S9#;hM0S0{V+*g`0dy#IIm O@<$8v_dm$r6aNc^Ggqnr literal 74164 zcmeFZ2UJwevM9XCS#pjNL?lTPB#MY66#>ZtA|hePISh=563^2eye&^nE&i&Va-@5Bxcdh%@d#8tgwuhu76!G15}!iH68;v zH~^rI{Q%c+AX3%G?kxa3eG1$G0Du_4#i0T4un-RR1BBTD_Nc>fL)Fk#C8@w?)8W$((!iivTFi^+;f%SuQC%6cjq z8rU*DD>rLvCwE(CkLx`^<~jE2jQ!!lB_VPbka?)_6#q~E*z_07_ZLVt!5%#m2h`wi zy(9U&i+&h*jlh-EP*JgXuB-i6na&(*i{okwo ze*>g%tlh1!MHpu+Zy&x^MGO@W1totpboevU7KFu=M46F7fB*e{b@z_QBEt z04mNdzHW9lwjNx{ST0#}X*gMl2yuzu6_W;lKRo|iuSYt6@e+sO$3N}`F9Co8?E3n; z^&j^%VE|B#eUh~KANOv{001=^0DNn9v2?TitGz!Giu)ETw}(XlKw%63v?JJOOx@o~ zi2qfNL;er|2o|rekFat}ln4NqAFr=3a<8v1^8f&E9soL=t~&r~Vge>y6FeMl0GApE zj~eH?2jIlAmjLIl^f$z@zj5*K2?&XZNl3}C73!(5Lj(^G7axy+;E!2*huyb`b7-jk3rFfuW-@ZI9SEpSIt>YlXBeOZ-9 zs*lywH8k~J=o=Vf$+dc8{np0T&feX_)63h(*Dv@(NN8AiL}WtZr=;YRs-dHDr} zMa3nhHMMp14UJ9BEnVF`y?y=P2ELDtPfSit|C*VFBUV<|);Bh{wo!*i$G=Zb(P!s> z=)wW;{zew|`)`!}zv!aI(uIqUkB3k6hb|mk?>~f7;}hHzBcxH*A+mI(Zq~Ipy8ZtNXHt;`?ai!^li@SJZMnhG|sr;zTNSI6YdfT242@EGK zaLV~aJpwVa7~$Nl-nyJWIX9%`jOPHm>A165xJn~6kKXcT?N;qB>P=g2}d z7d@olRvb@5-8Rf*F6rD3tWURQdO1iYByvYO8dg?0c%ZTV+L(Q)nxhZ}*M* z^}85ZbUNGuXKRVh(~NYRlVp3G$??EA@Z~i?mUQtgZj||*VQ1%7<{rO6tcRf?dA^Hk zKm!>o3ko`1s`L@vAH1{rD~0t)&+%};^DrR4C2xd2i0=GHW5!(~sW0WkRS(d1kGnT> zBS}ba=e|?A9{CTisl}NHtzwQ^3a^kChL=I=mKLjt^Y>KsnQgqT=%0&g|E|g@xJ;7r z{{;PtT#A;Ya5f*evS}U@e#`ACc>0M(`v)cGU7aivdYTfZ&6!P0w>E=YH$1pz=Qd`% z1GV(lWCcesM99WI7`5k&BhEWTD?{^U*%;Gd+GzZt_%$H)7j*ib4kjCD@XT^d|bi_Oe@Pq)yc=Z+vGIL z(??R2;@42I#XKAD-O6Wj)e7`#Uc5N-(`8_7Gzp^W!}+tsQlHq9KE2YRRpN@&U)@}0 zJ#zG<4>mOuhi+)d-7N^}c~Ni&8SD~!kSl*L@;p>&=Hmbh3?pPgMA5;Ztk=1zj=tTp*-6!n$M?dRP6wo<5N;dvOi;*>t&Xk|<6-Ehv+*9Vs!e&%E8Rq*^9_ z6}cntyu4rqzXl?zhs5}X{7>b!Bg&q=Sl=2lu3*8w z*Y~)Qx4-=NXIan6vJNOU3>}0RlI~)8G}7x{qG_dZi+}KG>CfJV_dfdaq^vcqB1!jO zq!kf(9h;Z0*_Ag}>7r|qWhQ79gnFBF#loEVfI^L#npDGb2z#oF$~$F(r*4W2$dm|q zP+4z|kEwRLPJ2o`n@WiJ?ygI5UaJ^xfriwNhI?fnDxTGLe;3V^Gp+b}&j+sr(af&> zL_;Sjg$t=%H5$WsaufC9q{JPOO=EYi(liixJ8iS%SjdELcqHD)T=X&=>~Hzzbdhe- zLHX1*OL18dA0?~Y92d4PYJi`fYC?7z3^^C~~tL7+qa>@*TASiE^U zEvwjQEu6X5%Cz3t^e}-Fqq>dqo)TagPCy601pvv{_@3_(?h{_yeKzYMN`HRNsJCj2YuUB`;RyVQZ}; z7)wh=+O6uI=46EG$I7)RkbHh<{2?=me3rcw+n!0rB_~8wWgCT-r5Dh~i^$Kg2KAc> zpHxowoChkz^|Ak@O2dgH|QIF+V(a1qWiqYUeGDaq55+g34Va|?bj-R-%-4UYH;7< zI`DbbP7Vgu+ji4Jpb9gw)Ga!ClCT|P{Yq7_R(Kzee7%l7h<+X{pJ#cjql%w>J^D}T z60ZEjpgE3Qg zO$Yh;s;8NTrH-$Go+|plpGtI|KJ~)>eX|&bllX9la$%1XEmNZoJnEvEpCtXf8OQ7Z z9%+@isdMvhWi-8yngzYlB3#w39_FL39sfz1KXI)#phmMG0U1tG#T^A=h6iN^ZV zngOS(Ly1a4rYhc70fGw z-loSv)6A{`6;h)t2WUG>ZCi7Wqn`+9?)yQJdk;ozMU=)z1T&hPc>BX1=iODWs-i0ixxot=utPP)3t-@Aq*kfnX@M@oFSR9m@vMGvT@sugU+-| zc~;w5>?-vKCEqMN|1RI!DY>`mh$S6P@d4;FDw|fDwm1CyKUD1z9j5}NkH3( zBxAxb#Kp+hU3FbDKKGsbX}ECqer)p!%dbGM)IH=WZ5#dl@lUT4qUw_?O-v3-))aZB zt1x<1+Z;R-zv`z7-1&YKjeK*X^S|A}(N%4m+Y%R(F`vK2U-oBadidWxGqw0D5rf8e zSPbFtsXLFTBF@b?V-f9`voBX+M+MG5-EkGC|=bAti-DjAs4z z?WV&Sh}0s??SAXQ%;gm|#t`ifD%pMxN^Y4d%=tLytNcVLzl=vd=+i#a)_6exHH!1e zxW~aP1fFhp@^OyYu@WosgM#TYQB9o zyRc%dK@R4Il2Oqm#wrMa11=tb<)*G=eE9oWUY_dNv5-rs`3f|8yn0K6Q_n=`*cv3m z`D9wSRP(1+p5mfZO`CX`7NyMLg^435fKkvCi9b5@b!~;EV{QM>``1MCGmWluLe@KjxMeCeA|qs8qL2 zsv&J~>3DVAxk40!E?E)g@6VEiqHcucn{FmcP*t0neu?ude309A%R zE-GC4&^L;`x0%noO1DTxrrceKw`{o-zO}i=%(p$R7V_@LYBjPK!M-H!{PX9=m#beK zd?@K4dGBRy4jf?)1B7OL)8}94&)Dw&B#D z^JB*}MBwiWUYrhZVA#8j+7fwkKJ?Cf7dYZ8HB7W0eim~J^4Jo6fS0*a6jFG`K3$tQ zo@(bQ{JVYLf2(%scbOKdC@ApemKZyw9`bi~=bn@lB{`_Ypw@PZ^5h$7z989mUCEdi zZv=qlRq!veGaO5}u_T1n=b}dux3O*Fihaw?z9pcrhUaYq4}KTa<}n|UImQM*^8Ao8 zYBqZ>LNV$>$zlNwQLCAB3c13XIl=?Y?Xll{_qjB3Uqmc?qS3m4FhUi;k6?^&Y%}ER z-?h7tvO~?yZ6Tql74t$u4JXvW4R5my*l~U$q%|bNfEC`0?b>yHt`m%fv&vGB^{3L& zEHZks68rI40j0`W-f<=eip)LNWB+8S z{7Xz7i9xu;sv*>p;HKee<|N8Cr3Oarw$#Ox1{+u8`a3M<)ov4t~JscKDJ`R>xab`9% zwVUSP()bWwqZK~8thy#x+m|Y^u72UV2$h|btWRP#R>tR`@U3WTnSO`SD)MRU9%FKr zENgNWw(bKr=eP{UFK96FG$EiM&Y(=zi5%o_vv$hOEW#Cg=CoLgw3#W*k;DtLPpvSs zOYci_)CWjSV~2vt9Q;}Hvc?tHPNPn@HzLL!^a9#auQmPR!fc_Z$isS}n;^PapKF8H z2D@^@YJaEH3q_21VmFjEX%%@@X2(!sMHdIv@>zdt5Z<71ll{h+@14hPZlE1U!IRC) ztVIfK1ZB89XDNTIROTH8QN!_NTr(vdqab4TEbZH0@QZE2zZHAILjsM_EB!2uaq_D7 zT1zF$U->c^R7fTkBz<~wcCT8#Aful&vn(;gFjHPPf1*SxFnModJMcpd=N_qEx7G3) z%W36-_o70@Xk}QXootNe_GB6;?J_jAOmpMT_W9g3htmD_q1 z@3d^o#W+S5x!#`r0&?Hg0%ygo+@|Kk%jMtE3GLIF4WO!J$o2UDF2eW6@$?@B5Ok-e zW|f1u5|twL5HT;4Klw9KDuaFW=xu9`hSkl4yH6fn151ianyU-MQpd?f1?8pR2R{aY z`sx=*wV27{abNRaom~SvhTq?&TJUX;ns}PQmLcZZV^{1x(be|%ovcL{9hsHtSFeHg zncu&c;?+9K%pDDVzDa(TI@;J1<}bOP_}_^+|HL3|CpJTYk}WKQlJKeAD7>FEm+G08 zjoELgviua;JnMgn#3YNnjnMdzeX(&3RJ>f8i3t#PFX+F0=R2|Gd*bEDHP((6keKB< zdB!pa(U!&ir~$U3A+<;6ob4{KsF9oxFC?r5H03mm8`}1#%3$U#2iKGTC00OpFO#ll zz>4UosYj>rsXC}Sj!y|IBu`}c!%E2f)$0Q)BZ1(N;Nya-dWgL? zzIMQhD4FdQ@krZTdJJD?#qr>C=5N+#sjtl!3!zU?oFC-%;ABa)sAq}31y8xY3(pI4 zAeCCwy94njy6iP18pXeFFYi1{oK&4X83$$T+ewSK#%F7u^LJm}pV$q=2Psm=bqf1i zySwyalvk@m2_0bpm>T(>6`P-8Sy|8^_)}K3loK_SNR@C*?UE|^d&*N%=83l=_#d3` zvicG;8_gUXRRm&e>^&8`(?`opi?T~c(2J4hoZW%46WH}@kl)6KJfxO|vDve~b$~k; zcyac|`Pn`23Ug)L^1<#kz!=-ch!CarN$59h%o>}2n(wRrI(GMy`(bEIbe;>|nxt&R zt<|S&xjqqHSvGF4AkG+DoPNhZ0E_Y2vCE`E@hjr1(OTFkma zYr_Uq?B&GC(d;l^#Sdq?(fhIL3z{vm8T8ij@3Ai5vCSiXO_wQ+ToyrhL*#ITB(YOV*Qysst0fJ+F>0T;) zYM#6=s3buec}ayXhSf@UnFUhb04Y(vY0m<^X`(UJoV&XK^_|#2R;2~r_UT_v373>{ zRAlp!UFklL{K=t}`tq)An0|jczeRyHY1>qdO?}(|&xeiZozCw{*6tSL@J57>F#WU- zs6$a271@e5d!-kqW{g`sr2qEMj5#d ztMo}2G zN|a!KS8qE@$}rEzO|ZD3`ncVM&@`ok!WY&7{T#oOeBo78Tjz5=_D4^f;|$cOrBafI z6pu!|L^PB%4OIsz4elI9?KSGrMDEuwg>q7ZC0c4kc^c&A)kYI*yLQV?~3a(S)Ky1pw`8b-JPCVI9_;YowoS!dA2@mu$JDyg=ODT)c6WF8S;Nq$}up?3%UKInzaYv~v>-X%?zq5gk6}^3{s+=#*+CVwxA)GYW@jHA4GH4M-kxY}m4J&zQBDg^G%qxr@>M9g7ku>xn6ImQm4qfeLi0@bcTk@ZAk2yP`Eo z$FC9|%CW-zbt_RuQ@Bs_$L)XHclopT@*lF@bGwP>__yx*>&X|CNxph6bqjJm^%4C@YiIOTChZg@{x%at}ZXXTOQd1U8h{xLoZ>lRjG1WQn+GDhv?)HSL1 zc`e=UDmhgWhjiR2>N=pdW#FuZ^gN}x(5-8n5H*Xb&#%qZmfrsqxG%0Et@v)*>f$cu z8B(<Zo~%rU6gjkR zEIP+yY37qP|6xnZS>6J6ZSTBn!3mZ}GZ&~UAq)%6>{%+iyXObUmN_jxw+7ss9)^&C z1@h5FC&efU(DRC3XrgcNHZCKxL4v@;LgnS<1{XJ(O33m-4@klLBsr!3iXR%R-Ow)m z<5p{uW$f+fCm(aXxyWzpL?41gyRzx(vcs0@oSG3ctBfYXCpiI*@>8ptg|oimybqhn zad4WpZ$D31*Yal>6z|r5apwhSrmsxHD>7Plydec5-X8_C7mY-LAGNoQ)mzkTB}4$Omc%xc6dhkM$5)

pf%ZBODct5AjT~d!bxe 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 Lzy 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 psh2k6}@{jva3eKHq5SOrrQ{ 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 h@93XA#Xmg8|8_ json) => + MobilityContext() + ..timestamp = json['timestamp'] == null + ? null + : DateTime.parse(json['timestamp'] as String) + ..date = + json['date'] == null ? null : DateTime.parse(json['date'] as String) + ..numberOfStops = (json['numberOfStops'] as num?)?.toInt() + ..numberOfMoves = (json['numberOfMoves'] as num?)?.toInt() + ..numberOfSignificantPlaces = + (json['numberOfSignificantPlaces'] as num?)?.toInt() + ..locationVariance = (json['locationVariance'] as num?)?.toDouble() + ..entropy = (json['entropy'] as num?)?.toDouble() + ..normalizedEntropy = (json['normalizedEntropy'] as num?)?.toDouble() + ..homeStay = (json['homeStay'] as num?)?.toDouble() + ..distanceTraveled = (json['distanceTraveled'] as num?)?.toDouble(); + +Map _$MobilityContextToJson(MobilityContext instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('timestamp', instance.timestamp?.toIso8601String()); + writeNotNull('date', instance.date?.toIso8601String()); + writeNotNull('numberOfStops', instance.numberOfStops); + writeNotNull('numberOfMoves', instance.numberOfMoves); + writeNotNull('numberOfSignificantPlaces', instance.numberOfSignificantPlaces); + writeNotNull('locationVariance', instance.locationVariance); + writeNotNull('entropy', instance.entropy); + writeNotNull('normalizedEntropy', instance.normalizedEntropy); + writeNotNull('homeStay', instance.homeStay); + writeNotNull('distanceTraveled', instance.distanceTraveled); + return val; +} + +GeoLocation _$GeoLocationFromJson(Map json) => GeoLocation( + (json['latitude'] as num).toDouble(), + (json['longitude'] as num).toDouble(), + )..$type = json['__type'] as String?; + +Map _$GeoLocationToJson(GeoLocation instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('__type', instance.$type); + val['latitude'] = instance.latitude; + val['longitude'] = instance.longitude; + return val; +} + +LocationSample _$LocationSampleFromJson(Map json) => + LocationSample( + GeoLocation.fromJson(json['geoLocation'] as Map), + DateTime.parse(json['dateTime'] as String), + )..$type = json['__type'] as String?; + +Map _$LocationSampleToJson(LocationSample instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('__type', instance.$type); + val['dateTime'] = instance.dateTime.toIso8601String(); + val['geoLocation'] = instance.geoLocation.toJson(); + return val; +} + +Stop _$StopFromJson(Map json) => Stop( + GeoLocation.fromJson(json['geoLocation'] as Map), + DateTime.parse(json['arrival'] as String), + DateTime.parse(json['departure'] as String), + (json['placeId'] as num?)?.toInt() ?? -1, + )..$type = json['__type'] as String?; + +Map _$StopToJson(Stop instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('__type', instance.$type); + val['geoLocation'] = instance.geoLocation.toJson(); + val['placeId'] = instance.placeId; + val['arrival'] = instance.arrival.toIso8601String(); + val['departure'] = instance.departure.toIso8601String(); + return val; +} + +Place _$PlaceFromJson(Map json) => Place( + (json['id'] as num).toInt(), + (json['stops'] as List) + .map((e) => Stop.fromJson(e as Map)) + .toList(), + )..$type = json['__type'] as String?; + +Map _$PlaceToJson(Place instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('__type', instance.$type); + val['id'] = instance.id; + val['stops'] = instance.stops.map((e) => e.toJson()).toList(); + return val; +} + +Move _$MoveFromJson(Map json) => Move( + Stop.fromJson(json['stopFrom'] as Map), + Stop.fromJson(json['stopTo'] as Map), + (json['distance'] as num?)?.toDouble(), + )..$type = json['__type'] as String?; + +Map _$MoveToJson(Move instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('__type', instance.$type); + val['stopFrom'] = instance.stopFrom.toJson(); + val['stopTo'] = instance.stopTo.toJson(); + writeNotNull('distance', instance.distance); + return val; +} diff --git a/packages/mobility_features/lib/src/domain.dart b/packages/mobility_features/lib/src/domain.dart index 4f3071d6e..90b83401c 100644 --- a/packages/mobility_features/lib/src/domain.dart +++ b/packages/mobility_features/lib/src/domain.dart @@ -1,22 +1,6 @@ part of '../mobility_features.dart'; const int HOURS_IN_A_DAY = 24; -const String _LATITUDE = 'latitude', - _LONGITUDE = 'longitude', - _DATE_TIME = 'datetime', - _ARRIVAL = 'arrival', - _DEPARTURE = 'departure', - _PLACE_ID = 'place_id', - _STOP_FROM = 'stop_from', - _STOP_TO = 'stop_to', - _DISTANCE = 'distance', - _GEO_LOCATION = 'geo_location'; - -/// Interface for JSON serialization. -abstract interface class Serializable { - Map toJson(); - Serializable.fromJson(Map json); -} /// Interface representing a geo location. abstract interface class GeoSpatial { @@ -52,65 +36,41 @@ class Distance { /// A [GeoLocation] object contains a latitude and longitude /// and represents a 2D spatial coordinates -class GeoLocation implements Serializable, GeoSpatial { - late double _latitude; - late double _longitude; - - GeoLocation(this._latitude, this._longitude); - - factory GeoLocation.fromJson(Map x) { - num? lat = x[_LATITUDE] as double?; - num? lon = x[_LONGITUDE] as double?; - return GeoLocation(lat as double, lon as double); - } - - double get latitude => _latitude; +@JsonSerializable(includeIfNull: false, explicitToJson: true) +class GeoLocation extends Serializable implements GeoSpatial { + double latitude, longitude; - double get longitude => _longitude; + GeoLocation(this.latitude, this.longitude); @override GeoLocation get geoLocation => this; @override - Map toJson() => {_LATITUDE: latitude, _LONGITUDE: longitude}; + Function get fromJsonFunction => _$GeoLocationFromJson; + factory GeoLocation.fromJson(Map json) => + FromJsonFactory().fromJson(json); @override - String toString() => '($_latitude, $_longitude)'; + Map toJson() => _$GeoLocationToJson(this); + + @override + String toString() => '($latitude, $longitude)'; } /// A [LocationSample] holds a 2D [GeoLocation] spatial data point /// as well as a [DateTime] value s.t. it may be temporally ordered -class LocationSample implements Serializable, GeoSpatial, Timestamped { - DateTime _dateTime; - GeoLocation _geoLocation; - - LocationSample(this._geoLocation, this._dateTime); - +@JsonSerializable(includeIfNull: false, explicitToJson: true) +class LocationSample extends Serializable implements GeoSpatial, Timestamped { @override - DateTime get dateTime => _dateTime; - + DateTime dateTime; @override - GeoLocation get geoLocation => _geoLocation; + GeoLocation geoLocation; - double? get latitude => geoLocation.latitude; + LocationSample(this.geoLocation, this.dateTime); + double? get latitude => geoLocation.latitude; double? get longitude => geoLocation.longitude; - @override - Map toJson() => { - _GEO_LOCATION: geoLocation.toJson(), - _DATE_TIME: json.encode(dateTime.millisecondsSinceEpoch) - }; - - factory LocationSample.fromJson(Map json) { - /// Parse, i.e. perform type check - GeoLocation pos = - GeoLocation.fromJson(json[_GEO_LOCATION] as Map); - int millis = int.parse(json[_DATE_TIME] as String); - DateTime dt = DateTime.fromMillisecondsSinceEpoch(millis); - return LocationSample(pos, dt); - } - LocationSample addNoise() { double lat = geoLocation.latitude * 1.000001; double lon = geoLocation.longitude * 1.000001; @@ -118,7 +78,15 @@ class LocationSample implements Serializable, GeoSpatial, Timestamped { } @override - String toString() => '($latitude, $longitude) @ $_dateTime'; + Function get fromJsonFunction => _$LocationSampleFromJson; + factory LocationSample.fromJson(Map json) => + FromJsonFactory().fromJson(json); + + @override + Map toJson() => _$LocationSampleToJson(this); + + @override + String toString() => '($latitude, $longitude) @ $dateTime'; } /// A [Stop] represents a cluster of [LocationSample] which were 'close' to each other @@ -126,12 +94,14 @@ class LocationSample implements Serializable, GeoSpatial, Timestamped { /// A [Stop] has an assigned [placeId] which links it to a [Place]. /// At initialization a stop will be assigned to the 'Noise' place (with id -1), /// and only after all places have been identified will a [Place] be assigned. -class Stop implements Serializable, GeoSpatial, Timestamped { - GeoLocation _geoLocation; - int _placeId = -1; - DateTime _arrival, _departure; +@JsonSerializable(includeIfNull: false, explicitToJson: true) +class Stop extends Serializable implements GeoSpatial, Timestamped { + @override + GeoLocation geoLocation; + int placeId = -1; + DateTime arrival, departure; - Stop(this._geoLocation, this._arrival, this._departure, [int _placeId = -1]); + Stop(this.geoLocation, this.arrival, this.departure, [int placeId = -1]); /// Construct stop from point cloud factory Stop.fromLocationSamples(List locationSamples, @@ -143,16 +113,7 @@ class Stop implements Serializable, GeoSpatial, Timestamped { } @override - DateTime get dateTime => _arrival; - - @override - GeoLocation get geoLocation => _geoLocation; - - DateTime get departure => _departure; - - DateTime get arrival => _arrival; - - int get placeId => _placeId; + DateTime get dateTime => arrival; List get hourSlots { int startHour = arrival.hour; @@ -189,62 +150,60 @@ class Stop implements Serializable, GeoSpatial, Timestamped { departure.millisecondsSinceEpoch - arrival.millisecondsSinceEpoch); @override - Map toJson() => { - _GEO_LOCATION: geoLocation.toJson(), - _PLACE_ID: placeId, - _ARRIVAL: arrival.millisecondsSinceEpoch, - _DEPARTURE: departure.millisecondsSinceEpoch - }; - - factory Stop.fromJson(Map json) => Stop( - GeoLocation.fromJson(json[_GEO_LOCATION] as Map), - DateTime.fromMillisecondsSinceEpoch(json[_ARRIVAL] as int), - DateTime.fromMillisecondsSinceEpoch(json[_DEPARTURE] as int), - json[_PLACE_ID] as int); + Function get fromJsonFunction => _$StopFromJson; + factory Stop.fromJson(Map json) => + FromJsonFactory().fromJson(json); + + @override + Map toJson() => _$StopToJson(this); @override String toString() => - 'Stop at place $placeId, (${_geoLocation.toString()}) [$arrival - $departure] (Duration: $duration) '; + 'Stop at place $placeId, (${geoLocation.toString()}) [$arrival - $departure] (Duration: $duration) '; } /// A [Place] is a cluster of [Stop]s found by the DBSCAN algorithm /// https://www.aaai.org/Papers/KDD/1996/KDD96-037.pdf -class Place { - final int _id; - final List _stops; +@JsonSerializable(includeIfNull: false, explicitToJson: true) +class Place extends Serializable { + final int id; + final List stops; GeoLocation? _geoLocation; - Place(this._id, this._stops); + Place(this.id, this.stops); - Duration get duration => - _stops.map((s) => s.duration).reduce((a, b) => a + b); + Duration get duration => stops.map((s) => s.duration).reduce((a, b) => a + b); - Duration durationForDate(DateTime? d) => _stops + Duration durationForDate(DateTime? d) => stops .where((s) => s.arrival.midnight == d) .map((s) => s.duration) .fold(const Duration(), (a, b) => a + b); - int get id => _id; + GeoLocation? get geoLocation => _geoLocation ??= _computeCentroid(stops); - List get stops => _stops; - - GeoLocation? get geoLocation => _geoLocation ??= _computeCentroid(_stops); + @override + Function get fromJsonFunction => _$PlaceFromJson; + factory Place.fromJson(Map json) => + FromJsonFactory().fromJson(json); + @override + Map toJson() => _$PlaceToJson(this); @override String toString() => - 'Place ID: $_id, at ${geoLocation.toString()} ($duration)'; + 'Place ID: $id, at ${geoLocation.toString()} ($duration)'; } /// A [Move] is a transfer from one [Stop] to another. /// A set of features can be derived from this such as the haversine distance between /// the stops, the duration of the move, and thereby also the average travel speed. -class Move implements Serializable, Timestamped { +@JsonSerializable(includeIfNull: false, explicitToJson: true) +class Move extends Serializable implements Timestamped { Stop stopFrom, stopTo; /// The haversine distance through all the samples between the two stops double? distance; - Move(this.stopFrom, this.stopTo, this.distance); + Move(this.stopFrom, this.stopTo, [this.distance]); /// Create a Move with a path of samples between two stops factory Move.fromPath(Stop a, Stop b, List path) { @@ -279,31 +238,29 @@ class Move implements Serializable, Timestamped { DateTime get dateTime => stopFrom.arrival; @override - Map toJson() => { - _STOP_FROM: stopFrom.toJson(), - _STOP_TO: stopTo.toJson(), - _DISTANCE: distance - }; + Function get fromJsonFunction => _$MoveFromJson; + factory Move.fromJson(Map json) => + FromJsonFactory().fromJson(json); - factory Move.fromJson(Map json) => Move( - Stop.fromJson(json[_STOP_FROM] as Map), - Stop.fromJson(json[_STOP_TO] as Map), - json[_DISTANCE] as double); + @override + Map toJson() => _$MoveToJson(this); @override String toString() => 'Move (Place ${stopFrom.placeId} [${stopFrom.dateTime}] -> Place ${stopTo.placeId} [${stopTo.dateTime}]) ($duration) (${distance!.toInt()} meters)'; } -class _HourMatrix { - List> _matrix; +class HourMatrix { + List> matrix; late int _numberOfPlaces; - _HourMatrix(this._matrix) { - _numberOfPlaces = _matrix.first.length; + int get numberOfPlaces => _numberOfPlaces; + + HourMatrix(this.matrix) { + _numberOfPlaces = matrix.first.length; } - factory _HourMatrix.fromStops(List stops, int numPlaces) { + factory HourMatrix.fromStops(List stops, int numPlaces) { // Init 2d matrix with 24 rows and cols equal to number of places List> matrix = List.generate( HOURS_IN_A_DAY, (_) => List.filled(numPlaces, 0.0)); @@ -318,36 +275,34 @@ class _HourMatrix { } } } - return _HourMatrix(matrix); + return HourMatrix(matrix); } // ignore: unused_element - factory _HourMatrix.routineMatrix(List<_HourMatrix> matrices) { + factory HourMatrix.routineMatrix(List matrices) { int nDays = matrices.length; int nPlaces = matrices.first.matrix.first.length; List> avg = zeroMatrix(HOURS_IN_A_DAY, nPlaces); - for (_HourMatrix m in matrices) { + for (HourMatrix m in matrices) { for (int i = 0; i < HOURS_IN_A_DAY; i++) { for (int j = 0; j < nPlaces; j++) { avg[i][j] += m.matrix[i][j] / nDays; } } } - return _HourMatrix(avg); + return HourMatrix(avg); } - List> get matrix => _matrix; - /// Features int get homePlaceId { int startHour = 0, endHour = 6; - List hourSpentAtPlace = List.filled(_numberOfPlaces, 0.0); + List hourSpentAtPlace = List.filled(numberOfPlaces, 0.0); - for (int placeId = 0; placeId < _numberOfPlaces; placeId++) { + for (int placeId = 0; placeId < numberOfPlaces; placeId++) { for (int hour = startHour; hour < endHour; hour++) { - hourSpentAtPlace[placeId] += _matrix[hour][placeId]; + hourSpentAtPlace[placeId] += matrix[hour][placeId]; } } double timeSpentAtNight = hourSpentAtPlace.fold(0.0, (a, b) => a + b); @@ -360,7 +315,7 @@ class _HourMatrix { double get sum { double s = 0.0; for (int i = 0; i < HOURS_IN_A_DAY; i++) { - for (int j = 0; j < _numberOfPlaces; j++) { + for (int j = 0; j < numberOfPlaces; j++) { s += matrix[i][j]; } } @@ -368,10 +323,10 @@ class _HourMatrix { } /// Calculates the error between two matrices - double computeOverlap(_HourMatrix other) { + double computeOverlap(HourMatrix other) { /// Check that dimensions match assert(other.matrix.length == HOURS_IN_A_DAY && - other.matrix.first.length == _matrix.first.length); + other.matrix.first.length == matrix.first.length); double maxOverlap = min(sum, other.sum); @@ -381,7 +336,7 @@ class _HourMatrix { double overlap = 0.0; // for (int i = 0; i < HOURS_IN_A_DAY; i++) { - for (int j = 0; j < _numberOfPlaces; j++) { + for (int j = 0; j < numberOfPlaces; j++) { /// If overlap in time-place matrix, /// add the overlap to the total overlap. /// The overlap is equal to the minimum of the two quantities @@ -400,14 +355,14 @@ class _HourMatrix { String s = '\n'; s += 'Home place ID: $homePlaceId\n'; s += 'Matrix\t\t'; - for (int p = 0; p < _numberOfPlaces; p++) { + for (int p = 0; p < numberOfPlaces; p++) { s += 'Place $p\t\t'; } s += '\n'; for (int hour = 0; hour < HOURS_IN_A_DAY; hour++) { s += 'Hour ${hour.toString().padLeft(2, '0')}\t\t'; - for (double e in _matrix[hour]) { + for (double e in matrix[hour]) { s += '${e.toStringAsFixed(3)}\t\t'; } s += '\n'; diff --git a/packages/mobility_features/lib/src/file_util.dart b/packages/mobility_features/lib/src/file_util.dart deleted file mode 100644 index 4638589c1..000000000 --- a/packages/mobility_features/lib/src/file_util.dart +++ /dev/null @@ -1,42 +0,0 @@ -part of '../mobility_features.dart'; - -const String _LOCATION_SAMPLES_FILE = 'location_samples', - _STOPS_FILE = 'stops', - _MOVES_FILE = 'moves', - _TEST_DATA_PATH = 'test/testdata'; - -Future _fileReference(Type T) async { - bool isMobile = Platform.isAndroid || Platform.isIOS; - - /// If on a mobile device, use the path_provider plugin to access the - /// file system - String path; - if (isMobile) { - path = (await getApplicationDocumentsDirectory()).path; - } - - /// Otherwise if unit testing just use the normal file system - else { - path = _TEST_DATA_PATH; - } - - /// Decide which file to write to, depending on the type (T) - String type = _LOCATION_SAMPLES_FILE; - if (T == Move) { - type = _MOVES_FILE; - } else if (T == Stop) { - type = _STOPS_FILE; - } - - // Create a file reference - File reference = File('$path/$type.json'); - - // If it does not exist already, - // create it by writing an empty string to it - bool exists = reference.existsSync(); - if (!exists) { - reference.writeAsStringSync('', mode: FileMode.write); - } - - return reference; -} diff --git a/packages/mobility_features/lib/src/features.dart b/packages/mobility_features/lib/src/main.dart similarity index 79% rename from packages/mobility_features/lib/src/features.dart rename to packages/mobility_features/lib/src/main.dart index c97d5f8c5..74f10f916 100644 --- a/packages/mobility_features/lib/src/features.dart +++ b/packages/mobility_features/lib/src/main.dart @@ -3,9 +3,12 @@ part of '../mobility_features.dart'; /// Main entry for configuring and listening for mobility features. /// Used as a singleton `MobilityFeatures()`. class MobilityFeatures { + static final MobilityFeatures _instance = MobilityFeatures._(); + double _stopRadius = 5, _placeRadius = 50; Duration _stopDuration = const Duration(seconds: 20); + final _streamController = StreamController.broadcast(); StreamSubscription? _subscription; MobilitySerializer? _serializerSamples; late MobilitySerializer _serializerStops; @@ -13,7 +16,8 @@ class MobilityFeatures { List _stops = []; List _moves = []; List _places = []; - List _cluster = [], _buffer = [], _samples = []; + List _cluster = []; + final List _buffer = [], _samples = []; int _saveEvery = 10; bool debug = false; @@ -23,23 +27,30 @@ class MobilityFeatures { } } - // Outgoing stream - final _streamController = StreamController.broadcast(); - - Stream get contextStream => _streamController.stream; - // Private constructor - MobilityFeatures._(); - - // Private Singleton field - static final MobilityFeatures _instance = MobilityFeatures._(); + MobilityFeatures._() { + FromJsonFactory().registerAll([ + GeoLocation(0, 0), + LocationSample(GeoLocation(0, 0), DateTime.now()), + Stop(GeoLocation(0, 0), DateTime.now(), DateTime.now()), + Place(0, []), + Move(Stop(GeoLocation(0, 0), DateTime.now(), DateTime.now()), + Stop(GeoLocation(0, 0), DateTime.now(), DateTime.now())) + ]); + } /// Singleton instance of MobilityFeatures. factory MobilityFeatures() => _instance; - /// Listen to a Stream of [LocationSample]. - /// The subscription will be stored as a [StreamSubscription] - /// which may be cancelled later. + /// A stream of generated mobility context objects. + Stream get contextStream => _streamController.stream; + + /// Start listening to the [stream] of [LocationSample] updates. + /// This will start calculating [MobilityContext] instances, which will be + /// delivered on the [contextStream] stream. + /// + /// Use [stopListening] to stop listening to the location stream and hence stop + /// generating mobility context objects. Future startListening(Stream stream) async { await _handleInit(); @@ -79,8 +90,12 @@ class MobilityFeatures { _places = _findPlaces(_stops, placeRadius: _placeRadius); // Compute features - MobilityContext context = - MobilityContext._(_stops, _places, _moves, date); + MobilityContext context = MobilityContext.fromMobility( + date, + _stops, + _places, + _moves, + ); _streamController.add(context); } } @@ -131,22 +146,22 @@ class MobilityFeatures { void _clearEverything() { _print('cleared'); - _serializerStops.flush(); - _serializerMoves.flush(); - _stops = []; - _moves = []; - _places = []; - _cluster = []; - _buffer = []; + _serializerStops.clear(); + _serializerMoves.clear(); + _stops.clear(); + _moves.clear(); + _places.clear(); + _cluster.clear(); + _buffer.clear(); } /// Save a sample to the buffer and store samples on disk if buffer overflows void _addToBuffer(LocationSample sample) { _buffer.add(sample); if (_buffer.length >= _saveEvery) { - _serializerSamples!.save(_buffer); + _serializerSamples!.append(_buffer); _print('Stored buffer to disk'); - _buffer = []; + _buffer.clear(); } } @@ -170,28 +185,32 @@ class MobilityFeatures { _places = _findPlaces(_stops); // Store to disk - _serializerStops.flush(); - _serializerStops.save(_stops); + _serializerStops.clear(); + _serializerStops.append(_stops); // Extract date DateTime date = _cluster.last.dateTime.midnight; if (stopPrev != null) { _moves = _findMoves(_stops, _samples); - _serializerMoves.flush(); - _serializerMoves.save(_moves); + _serializerMoves.clear(); + _serializerMoves.append(_moves); } // Compute features - MobilityContext context = - MobilityContext._(_stops, _places, _moves, date); + MobilityContext context = MobilityContext.fromMobility( + date, + _stops, + _places, + _moves, + ); _streamController.add(context); } // Reset samples etc - _cluster = []; - _serializerSamples!.flush(); - _buffer = []; + _cluster.clear(); + _serializerSamples!.clear(); + _buffer.clear(); } /// Configure the stop-duration for the stop algorithm @@ -215,7 +234,7 @@ class MobilityFeatures { Future saveSamples(List samples) async { final serializer = await _locationSampleSerializer; - serializer.save(samples); + serializer.append(samples); } Future> loadSamples() async { diff --git a/packages/mobility_features/lib/src/context.dart b/packages/mobility_features/lib/src/mobility_context.dart similarity index 51% rename from packages/mobility_features/lib/src/context.dart rename to packages/mobility_features/lib/src/mobility_context.dart index 9a8b1588c..9a8cd4af0 100644 --- a/packages/mobility_features/lib/src/context.dart +++ b/packages/mobility_features/lib/src/mobility_context.dart @@ -1,132 +1,145 @@ part of '../mobility_features.dart'; -/// Daily mobility context. +/// Daily mobility context for a day on [date]. /// -/// All Stops and Moves are on the same [date]. -/// [places] are all places for which the duration on the given date is greater than 0. +/// All [stops] and [moves] are on the same [date]. +/// The [places] are all places for which the duration on the given [date] +/// is greater than 0. +@JsonSerializable(includeIfNull: false, explicitToJson: true) class MobilityContext { - late final DateTime _timestamp, _date; - final List _stops; - final List _places; - final List _moves; - late List _significantPlaces; + List? _significantPlaces; + HourMatrix? _hourMatrix; Place? _homePlace; - late _HourMatrix _hourMatrix; - double? _locationVariance, - _entropy, - _normalizedEntropy, - _homeStay, - _distanceTraveled; - - /// Private constructor. - MobilityContext._(this._stops, this._places, this._moves, this._date) { - _timestamp = DateTime.now(); - - // compute all the features - _significantPlaces = - _places.where((p) => p.duration > const Duration(minutes: 3)).toList(); - _hourMatrix = _HourMatrix.fromStops(_stops, _places.length); - _homePlace = _findHomePlaceToday(); - _homeStay = _calculateHomeStay(); - _locationVariance = _calculateLocationVariance(); - _entropy = _calculateEntropy(); - _normalizedEntropy = _calculateNormalizedEntropy(); - _distanceTraveled = _calculateDistanceTraveled(); - } - - /// The date of this context. - DateTime get date => _date; + List? _stops; + List? _places; + List? _moves; /// Timestamp at which the features were computed - DateTime get timestamp => _timestamp; + DateTime? timestamp; - /// Stops today. - List get stops => _stops; - - /// Moves today. - List get moves => _moves; + /// The date of this context. + DateTime? date; - /// Places today. - List get places => _places; + /// Number of stops made today. + int? numberOfStops; - /// All significant places, i.e. places with a minimum stay duration. - List get significantPlaces => _significantPlaces; + /// Number of moves made today. + int? numberOfMoves; /// Number of significant places visited today. - int get numberOfSignificantPlaces => _significantPlaces.length; - - /// Home place. - /// Returns null if home cannot be found from the available data. - Place? get homePlace => _homePlace; - - /// Home Stay Percentage today. A scalar between 0 and 1. - /// Returns null if cannot be calculated based on the available data. - double? get homeStay => _homeStay; + int? numberOfSignificantPlaces; /// Location variance today. - double? get locationVariance => _locationVariance; + double? locationVariance; /// Location entropy. /// /// * High entropy: Time is spent evenly among all places /// * Low entropy: Time is mainly spent at a few of the places - double? get entropy => _entropy; + double? entropy; /// Normalized location entropy. A scalar between 0 and 1. - double? get normalizedEntropy => _normalizedEntropy; + double? normalizedEntropy; + + /// Home Stay Percentage today. A scalar between 0 and 1. + /// Returns null if cannot be calculated based on the available data. + double? homeStay; /// Distance traveled today in meters. - double? get distanceTraveled => _distanceTraveled; + double? distanceTraveled; + + MobilityContext(); + + MobilityContext.fromMobility( + this.date, + this._stops, + this._places, + this._moves, + ) { + timestamp = DateTime.now(); + + // compute all features + _significantPlaces = + _places!.where((p) => p.duration > const Duration(minutes: 3)).toList(); + _hourMatrix = HourMatrix.fromStops(_stops!, _places!.length); + numberOfSignificantPlaces = _significantPlaces!.length; + numberOfStops = _stops?.length; + numberOfMoves = _moves?.length; + _homePlace = _findHomePlaceToday(); + homeStay = _calculateHomeStay(); + locationVariance = _calculateLocationVariance(); + entropy = _calculateEntropy(); + normalizedEntropy = _calculateNormalizedEntropy(); + distanceTraveled = _calculateDistanceTraveled(); + } + + /// Stops today. + List? get stops => _stops; + + /// Places today. + List? get places => _places; + + /// Moves today. + List? get moves => _moves; + + /// The place used as 'home'. + /// Returns null if home cannot be found from the available data. + Place? get homePlace => _homePlace; + + /// All significant places, i.e. places with a minimum stay duration. + List? get significantPlaces => _significantPlaces; double? _calculateHomeStay() { - if (stops.isEmpty) return null; + if (_stops == null) return null; + if (_stops!.isEmpty) return null; // Latest known sample time - final latestTime = _stops.last.departure; + final latestTime = _stops!.last.departure; // Total time elapsed from midnight until the last stop int totalTime = latestTime.millisecondsSinceEpoch - latestTime.midnight.millisecondsSinceEpoch; // Find todays home id, if no home exists today return null - if (_hourMatrix.homePlaceId == -1) return null; + if (_hourMatrix!.homePlaceId == -1) return null; - int homeTime = stops - .where((s) => s.placeId == _hourMatrix.homePlaceId) + int homeTime = _stops! + .where((s) => s.placeId == _hourMatrix!.homePlaceId) .map((s) => s.duration.inMilliseconds) .fold(0, (a, b) => a + b); return homeTime.toDouble() / totalTime.toDouble(); } - Place? _findHomePlaceToday() => (_hourMatrix.homePlaceId == -1) + Place? _findHomePlaceToday() => (_hourMatrix!.homePlaceId == -1) ? null - : _places.where((p) => p._id == _hourMatrix.homePlaceId).first; + : _places!.where((p) => p.id == _hourMatrix!.homePlaceId).first; double? _calculateLocationVariance() { // Require at least 2 observations - if (_stops.length < 2) return 0.0; + if (_stops!.length < 2) return 0.0; - double latStd = Stats.fromData(_stops.map((s) => (s.geoLocation.latitude))) + double latStd = Stats.fromData(_stops!.map((s) => (s.geoLocation.latitude))) .standardDeviation as double; - double lonStd = Stats.fromData(_stops.map((s) => (s.geoLocation.longitude))) - .standardDeviation as double; + double lonStd = + Stats.fromData(_stops!.map((s) => (s.geoLocation.longitude))) + .standardDeviation as double; return log(latStd * latStd + lonStd * lonStd + 1); } double? _calculateEntropy() { // if no places were visited return null // else - the Entropy is zero when one outcome is certain to occur - if (places.isEmpty) { + if (_places!.isEmpty) { return null; - } else if (places.length == 1) { + } else if (_places!.length == 1) { return 0.0; } // calculate time spent at different places List durations = - places.map((p) => p.durationForDate(date)).toList(); + _places!.map((p) => p.durationForDate(date)).toList(); Duration totalTimeSpent = durations.fold(const Duration(), (a, b) => a + b); @@ -139,22 +152,14 @@ class MobilityContext { } double _calculateNormalizedEntropy() => - (places.length == 1) ? 0.0 : entropy! / log(places.length); + (_places!.length == 1) ? 0.0 : entropy! / log(_places!.length); double _calculateDistanceTraveled() => - _moves.map((m) => (m.distance)).fold(0.0, (a, b) => a + b!); - - Map toJson() => { - "date": date.toIso8601String(), - "computed_at": timestamp.toIso8601String(), - "num_of_stops": stops.length, - "num_of_moves": moves.length, - "num_of_significant_places": significantPlaces.length, - "normalized_entropy": normalizedEntropy, - "home_stay": homeStay, - "distance_traveled": distanceTraveled, - "location_variance": locationVariance - }; + _moves!.map((m) => (m.distance)).fold(0.0, (a, b) => a + b!); + + factory MobilityContext.fromJson(Map json) => + _$MobilityContextFromJson(json); + Map toJson() => _$MobilityContextToJson(this); } // /// Routine index (overlap) calculation diff --git a/packages/mobility_features/lib/src/intermediate.dart b/packages/mobility_features/lib/src/mobility_functions.dart similarity index 99% rename from packages/mobility_features/lib/src/intermediate.dart rename to packages/mobility_features/lib/src/mobility_functions.dart index f72e61138..ba6980f5e 100644 --- a/packages/mobility_features/lib/src/intermediate.dart +++ b/packages/mobility_features/lib/src/mobility_functions.dart @@ -1,43 +1,5 @@ part of '../mobility_features.dart'; -/// Find the stops in a sequence of gps data points -//List _findStops(List data, -// {double stopRadius = 25.0, -// Duration stopDuration = const Duration(minutes: 3)}) { -// if (data.isEmpty) return []; -// -// List stops = []; -// int n = data.length; -// -// /// Go through all the location samples, i.e from index [0...n-1] -// int start = 0; -// while (start < n) { -// int end = start + 1; -// List subset = data.sublist(start, end); -// GeoLocation centroid = _computeCentroid(subset); -// -// /// Expand cluster until either all samples have been considered, -// /// or the current sample lies outside the radius. -// while ( -// end < n && Distance.fromGeospatial(centroid, data[end]) <= stopRadius) { -// end += 1; -// subset = data.sublist(start, end); -// centroid = _computeCentroid(subset); -// } -// Stop s = Stop._fromLocationSamples(subset); -// stops.add(s); -// -// /// Update the start index, such that we no longer look at -// /// the previously considered data samples -// start = end; -// } -// -// /// Filter out stops which are shorter than the min. duration -// stops = stops.where((s) => (s.duration >= stopDuration)).toList(); -// -// return stops; -//} - /// Finds the places by clustering stops with the DBSCAN algorithm List _findPlaces(List stops, {double placeRadius = 50.0}) { List places = []; @@ -71,7 +33,7 @@ List _findPlaces(List stops, {double placeRadius = 50.0}) { /// Set placeId field for the stops belonging to this place for (var s in stopsForPlace) { - s._placeId = p.id; + s.placeId = p.id; } } return places; @@ -106,3 +68,41 @@ GeoLocation _computeCentroid(List data) { return GeoLocation(lat, lon); } + +/// Find the stops in a sequence of gps data points +//List _findStops(List data, +// {double stopRadius = 25.0, +// Duration stopDuration = const Duration(minutes: 3)}) { +// if (data.isEmpty) return []; +// +// List stops = []; +// int n = data.length; +// +// /// Go through all the location samples, i.e from index [0...n-1] +// int start = 0; +// while (start < n) { +// int end = start + 1; +// List subset = data.sublist(start, end); +// GeoLocation centroid = _computeCentroid(subset); +// +// /// Expand cluster until either all samples have been considered, +// /// or the current sample lies outside the radius. +// while ( +// end < n && Distance.fromGeospatial(centroid, data[end]) <= stopRadius) { +// end += 1; +// subset = data.sublist(start, end); +// centroid = _computeCentroid(subset); +// } +// Stop s = Stop._fromLocationSamples(subset); +// stops.add(s); +// +// /// Update the start index, such that we no longer look at +// /// the previously considered data samples +// start = end; +// } +// +// /// Filter out stops which are shorter than the min. duration +// stops = stops.where((s) => (s.duration >= stopDuration)).toList(); +// +// return stops; +//} diff --git a/packages/mobility_features/lib/src/serializer.dart b/packages/mobility_features/lib/src/serializer.dart index 5bef71c57..9873f6ba0 100644 --- a/packages/mobility_features/lib/src/serializer.dart +++ b/packages/mobility_features/lib/src/serializer.dart @@ -1,5 +1,6 @@ part of '../mobility_features.dart'; +/// Utility class for (de)serializing [Stop] and [Move] objects. class MobilitySerializer { // Provide a file reference in order to serialize objects. File? _file; @@ -7,23 +8,23 @@ class MobilitySerializer { MobilitySerializer(); - /// Deletes the content of the file - void flush() => _file!.writeAsStringSync('', mode: FileMode.write); + /// Clears (deletes) the content of [file] + void clear() => _file!.writeAsStringSync('', mode: FileMode.write); Future get file async => _file ??= await _fileReference(T); - /// Writes a list of [Serializable] to the file given in the constructor. - void save(List elements) async { + /// Appends a list of mobility [elements] to [file]. + void append(List elements) async { File f = await file; String jsonString = ""; - for (Serializable e in elements) { - jsonString += json.encode(e.toJson()) + delimiter; + for (Serializable element in elements) { + jsonString += json.encode(element.toJson()) + delimiter; } f.writeAsStringSync(jsonString, mode: FileMode.writeOnlyAppend); } /// Reads contents of the [file] and maps it to a list of a specific - /// [Serializable] type. + /// mobility objects. Future> load() async { File f = await file; @@ -53,3 +54,40 @@ class MobilitySerializer { } } } + +const String _LOCATION_SAMPLES_FILE = 'location_samples', + _STOPS_FILE = 'stops', + _MOVES_FILE = 'moves', + _TEST_DATA_PATH = 'test/testdata'; + +Future _fileReference(Type T) async { + bool isMobile = Platform.isAndroid || Platform.isIOS; + + /// If on a mobile device, use the path_provider plugin to access the + /// file system. Otherwise if unit testing just use the normal file system. + String path; + if (isMobile) { + path = (await getApplicationDocumentsDirectory()).path; + } else { + path = _TEST_DATA_PATH; + } + + /// Decide which file to write to, depending on the type (T) + String type = _LOCATION_SAMPLES_FILE; + if (T == Move) { + type = _MOVES_FILE; + } else if (T == Stop) { + type = _STOPS_FILE; + } + + // Create a file reference + File reference = File('$path/$type.json'); + + // If it does not exist already, create it by writing an empty string to it. + bool exists = reference.existsSync(); + if (!exists) { + reference.writeAsStringSync('', mode: FileMode.write); + } + + return reference; +} diff --git a/packages/mobility_features/lib/src/functions.dart b/packages/mobility_features/lib/src/util.dart similarity index 100% rename from packages/mobility_features/lib/src/functions.dart rename to packages/mobility_features/lib/src/util.dart diff --git a/packages/mobility_features/mobility_features_example/ios/Podfile b/packages/mobility_features/mobility_features_example/ios/Podfile deleted file mode 100644 index 1e8c3c90a..000000000 --- a/packages/mobility_features/mobility_features_example/ios/Podfile +++ /dev/null @@ -1,41 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '9.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/packages/mobility_features/pubspec.yaml b/packages/mobility_features/pubspec.yaml index e438b2b76..0d254b9d4 100644 --- a/packages/mobility_features/pubspec.yaml +++ b/packages/mobility_features/pubspec.yaml @@ -1,6 +1,6 @@ name: mobility_features description: Calculation of real-time mobility features like places, stops, and home stay -version: 5.0.0 +version: 6.0.0 homepage: https://github.com/cph-cachet/flutter-plugins/ environment: @@ -13,10 +13,14 @@ dependencies: simple_cluster: ^0.3.0 stats: ^2.0.0 path_provider: ^2.0.2 + json_annotation: ^4.8.0 + carp_serializable: ^2.0.0 dev_dependencies: flutter_test: sdk: flutter flutter_lints: any + json_serializable: any + build_runner: any flutter: diff --git a/packages/mobility_features/test/mobility_features_test.dart b/packages/mobility_features/test/mobility_features_test.dart index 542b589a8..9cf1b8d87 100644 --- a/packages/mobility_features/test/mobility_features_test.dart +++ b/packages/mobility_features/test/mobility_features_test.dart @@ -3,13 +3,13 @@ library mobility_test; import 'dart:async'; import 'package:mobility_features/mobility_features.dart'; import 'dart:io'; -import 'package:flutter_test/flutter_test.dart'; import 'dart:convert'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:carp_serializable/carp_serializable.dart'; part 'test_utils.dart'; void main() async { - JsonEncoder jsonEncoder = const JsonEncoder.withIndent('\t'); DateTime jan01 = DateTime(2020, 01, 01); // Poppelgade 7, home @@ -221,8 +221,8 @@ void main() async { // Listen to the Context stream Stream contextStream = MobilityFeatures().contextStream; contextStream.listen(expectAsync1((c) { - printList(c.stops); - print(c.toJson()); + printList(c.stops!); + print(toJsonString(c.toJson())); }, count: expectedContexts)); // Stream all the samples one by one @@ -235,7 +235,7 @@ void main() async { test('Stream LocationSamples with path between locations', () async { void onContext(MobilityContext mc) { print(mc.toJson()); - printList(mc.stops); + printList(mc.stops!); } flushFiles(); @@ -378,7 +378,7 @@ void main() async { /// Listen to the Context stream Stream contextStream = MobilityFeatures().contextStream; contextStream.listen( - expectAsync1((c) => printList(c.stops), count: expectedContexts)); + expectAsync1((c) => printList(c.stops!), count: expectedContexts)); // Stream all the samples one by one for (LocationSample s in samples) { @@ -410,22 +410,22 @@ void main() async { mobilityStream.listen(expectAsync1((event) { print('=' * 150); - print( - "Mobility Context Received: ${jsonEncoder.convert(event.toJson())}"); + print("Mobility Context Received: ${toJsonString(event.toJson())}"); print('-' * 50); print("STOPS"); - printList(event.stops); + printList(event.stops!); print("ALL PLACES"); - printList(event.places); + printList(event.places!); print("SIGNIFICANT PLACES"); - printList(event.significantPlaces); + printList(event.significantPlaces!); print( - "TOTAL DURATION (STOPS): ${event.stops.map((p) => p.duration).reduce((a, b) => a + b)}"); + "TOTAL DURATION (STOPS): ${event.stops?.map((p) => p.duration).reduce((a, b) => a + b)}"); }, count: 283)); - onLastDate.forEach((e) { + for (var e in onLastDate) { controller.add(e); - }); + } + controller.close(); }); }); diff --git a/packages/mobility_features/test/testdata/location_samples.json b/packages/mobility_features/test/testdata/location_samples.json index 1a90ddbb4..e69de29bb 100644 --- a/packages/mobility_features/test/testdata/location_samples.json +++ b/packages/mobility_features/test/testdata/location_samples.json @@ -1,110 +0,0 @@ -{"geo_location":{"latitude":48.171281653157905,"longitude":11.563218639973703},"datetime":"1581633183541"} -{"geo_location":{"latitude":48.1713003791387,"longitude":11.563167221184242},"datetime":"1581633189548"} -{"geo_location":{"latitude":48.17127219694427,"longitude":11.563203045308759},"datetime":"1581633195552"} -{"geo_location":{"latitude":48.17126526291558,"longitude":11.56321206673613},"datetime":"1581633198223"} -{"geo_location":{"latitude":48.17126389624605,"longitude":11.563214525559026},"datetime":"1581633198223"} -{"geo_location":{"latitude":48.171286671508035,"longitude":11.563189301036731},"datetime":"1581633201547"} -{"geo_location":{"latitude":48.17128164382298,"longitude":11.563204642645978},"datetime":"1581633207537"} -{"geo_location":{"latitude":48.17129498783873,"longitude":11.563212135816345},"datetime":"1581633213544"} -{"geo_location":{"latitude":48.17128662628747,"longitude":11.563238443120047},"datetime":"1581633219550"} -{"geo_location":{"latitude":48.171306394859734,"longitude":11.563227110801288},"datetime":"1581633225545"} -{"geo_location":{"latitude":48.17129425356578,"longitude":11.563191806114972},"datetime":"1581633230140"} -{"geo_location":{"latitude":48.171293689458764,"longitude":11.563196412330011},"datetime":"1581633286547"} -{"geo_location":{"latitude":48.17128943954254,"longitude":11.563195460232395},"datetime":"1581633288141"} -{"geo_location":{"latitude":48.17128943954254,"longitude":11.563195460232395},"datetime":"1581633294141"} -{"geo_location":{"latitude":48.17128943954254,"longitude":11.563195460232395},"datetime":"1581633300141"} -{"geo_location":{"latitude":48.17128943954254,"longitude":11.563195460232395},"datetime":"1581633301141"} -{"geo_location":{"latitude":48.17128943954254,"longitude":11.563195460232395},"datetime":"1581633302144"} -{"geo_location":{"latitude":48.17128943954254,"longitude":11.563195460232395},"datetime":"1581633305142"} -{"geo_location":{"latitude":48.17128943954254,"longitude":11.563195460232395},"datetime":"1581633311141"} -{"geo_location":{"latitude":48.17128943954254,"longitude":11.563195460232395},"datetime":"1581633318141"} -{"geo_location":{"latitude":48.17128943954254,"longitude":11.563195460232395},"datetime":"1581633324143"} -{"geo_location":{"latitude":48.17128943954254,"longitude":11.563195460232395},"datetime":"1581633330142"} -{"geo_location":{"latitude":48.17128943954254,"longitude":11.563195460232395},"datetime":"1581633335144"} -{"geo_location":{"latitude":48.17128943954254,"longitude":11.563195460232395},"datetime":"1581633341141"} -{"geo_location":{"latitude":48.17128943954254,"longitude":11.563195460232395},"datetime":"1581633348141"} -{"geo_location":{"latitude":48.17128127572143,"longitude":11.56317741341416},"datetime":"1581633364555"} -{"geo_location":{"latitude":48.17130290793368,"longitude":11.563169528635354},"datetime":"1581633370544"} -{"geo_location":{"latitude":48.17128704926877,"longitude":11.563200777109483},"datetime":"1581633376549"} -{"geo_location":{"latitude":48.17129273842976,"longitude":11.56321956053865},"datetime":"1581633382553"} -{"geo_location":{"latitude":48.17129449851463,"longitude":11.563224853290832},"datetime":"1581633383188"} -{"geo_location":{"latitude":48.17129497397242,"longitude":11.563226234435081},"datetime":"1581633383188"} -{"geo_location":{"latitude":48.17128941653813,"longitude":11.563191167393118},"datetime":"1581633388141"} -{"geo_location":{"latitude":48.17128941653813,"longitude":11.563191167393118},"datetime":"1581633389142"} -{"geo_location":{"latitude":48.17128941653813,"longitude":11.563191167393118},"datetime":"1581633394141"} -{"geo_location":{"latitude":48.171290987350105,"longitude":11.563187340478425},"datetime":"1581633407559"} -{"geo_location":{"latitude":48.17128583500631,"longitude":11.56315969457978},"datetime":"1581633413554"} -{"geo_location":{"latitude":48.171286973233535,"longitude":11.56316378379428},"datetime":"1581633419548"} -{"geo_location":{"latitude":48.17128676487958,"longitude":11.56316469101152},"datetime":"1581633424195"} -{"geo_location":{"latitude":48.171286663382034,"longitude":11.563165235349599},"datetime":"1581633424195"} -{"geo_location":{"latitude":48.17129130944989,"longitude":11.563178715745625},"datetime":"1581633425560"} -{"geo_location":{"latitude":48.17130733749713,"longitude":11.563212775898416},"datetime":"1581633431552"} -{"geo_location":{"latitude":48.17130542586099,"longitude":11.563196774852665},"datetime":"1581633437560"} -{"geo_location":{"latitude":48.17129326570097,"longitude":11.563160108856371},"datetime":"1581633443550"} -{"geo_location":{"latitude":48.171291635570256,"longitude":11.563188545890023},"datetime":"1581633449635"} -{"geo_location":{"latitude":48.17129870532947,"longitude":11.56321129357901},"datetime":"1581633455545"} -{"geo_location":{"latitude":48.171314378413,"longitude":11.563190104015941},"datetime":"1581633461550"} -{"geo_location":{"latitude":48.171312552986805,"longitude":11.563188678575338},"datetime":"1581633467555"} -{"geo_location":{"latitude":48.17129109679785,"longitude":11.563196082530137},"datetime":"1581633473552"} -{"geo_location":{"latitude":48.17132280064145,"longitude":11.563191398671746},"datetime":"1581633479552"} -{"geo_location":{"latitude":48.1712941704202,"longitude":11.56321293837407},"datetime":"1581633485549"} -{"geo_location":{"latitude":48.171299601547645,"longitude":11.563196226678375},"datetime":"1581633491551"} -{"geo_location":{"latitude":48.17131438615358,"longitude":11.563168362464546},"datetime":"1581633497551"} -{"geo_location":{"latitude":48.17128123960417,"longitude":11.563189026178073},"datetime":"1581633503553"} -{"geo_location":{"latitude":48.17132011829338,"longitude":11.563226292518822},"datetime":"1581633509538"} -{"geo_location":{"latitude":48.17130252395954,"longitude":11.563211057067306},"datetime":"1581633515540"} -{"geo_location":{"latitude":48.17129798979105,"longitude":11.563191975940216},"datetime":"1581633521548"} -{"geo_location":{"latitude":48.17131656003656,"longitude":11.563210741303022},"datetime":"1581633527544"} -{"geo_location":{"latitude":48.17131863514652,"longitude":11.56319862504117},"datetime":"1581633533545"} -{"geo_location":{"latitude":48.1713070561953,"longitude":11.563198433655048},"datetime":"1581633539543"} -{"geo_location":{"latitude":48.17130236927287,"longitude":11.563201300216122},"datetime":"1581633545531"} -{"geo_location":{"latitude":48.17129521056969,"longitude":11.56319943699132},"datetime":"1581633550540"} -{"geo_location":{"latitude":48.17131673866101,"longitude":11.563201412586812},"datetime":"1581633556539"} -{"geo_location":{"latitude":48.17130397034964,"longitude":11.563203172068816},"datetime":"1581633562551"} -{"geo_location":{"latitude":48.171288536883395,"longitude":11.563204886747661},"datetime":"1581633568553"} -{"geo_location":{"latitude":48.17130760267435,"longitude":11.563196954817178},"datetime":"1581633574547"} -{"geo_location":{"latitude":48.171280680863305,"longitude":11.563215972877181},"datetime":"1581633580542"} -{"geo_location":{"latitude":48.171296080313326,"longitude":11.56322328691332},"datetime":"1581633586541"} -{"geo_location":{"latitude":48.17132510439827,"longitude":11.563184492969626},"datetime":"1581633592554"} -{"geo_location":{"latitude":48.171309745958155,"longitude":11.563212691472879},"datetime":"1581633598543"} -{"geo_location":{"latitude":48.17130980868023,"longitude":11.563223221990256},"datetime":"1581633604549"} -{"geo_location":{"latitude":48.17130116552479,"longitude":11.563204770731598},"datetime":"1581633610547"} -{"geo_location":{"latitude":48.17132024048344,"longitude":11.563197098288784},"datetime":"1581633616553"} -{"geo_location":{"latitude":48.171299319142854,"longitude":11.563184923043417},"datetime":"1581633622547"} -{"geo_location":{"latitude":48.171310790416754,"longitude":11.563198310967506},"datetime":"1581633628544"} -{"geo_location":{"latitude":48.17131589082105,"longitude":11.563213775430425},"datetime":"1581633634615"} -{"geo_location":{"latitude":48.17131124461477,"longitude":11.5632183996362},"datetime":"1581633640550"} -{"geo_location":{"latitude":48.17132126286372,"longitude":11.563221820856478},"datetime":"1581633646551"} -{"geo_location":{"latitude":48.17130773221799,"longitude":11.56319857289543},"datetime":"1581633652555"} -{"geo_location":{"latitude":48.17133161288919,"longitude":11.563206664336509},"datetime":"1581633658545"} -{"geo_location":{"latitude":48.17132725995403,"longitude":11.563204133995162},"datetime":"1581633664545"} -{"geo_location":{"latitude":48.17131174010998,"longitude":11.563214543661774},"datetime":"1581633670547"} -{"geo_location":{"latitude":48.17131006002293,"longitude":11.563231760120583},"datetime":"1581633676549"} -{"geo_location":{"latitude":48.17131857591096,"longitude":11.563203957262328},"datetime":"1581633682549"} -{"geo_location":{"latitude":48.1713001793906,"longitude":11.563207264402903},"datetime":"1581633688547"} -{"geo_location":{"latitude":48.171320995827394,"longitude":11.56320571687284},"datetime":"1581633694551"} -{"geo_location":{"latitude":48.171289834325194,"longitude":11.563235278192037},"datetime":"1581633700537"} -{"geo_location":{"latitude":48.171305118409826,"longitude":11.563221094539754},"datetime":"1581633706547"} -{"geo_location":{"latitude":48.171306756445375,"longitude":11.563214587831721},"datetime":"1581633712549"} -{"geo_location":{"latitude":48.17129139009002,"longitude":11.563199191004612},"datetime":"1581633718547"} -{"geo_location":{"latitude":48.17131649786042,"longitude":11.56319249730382},"datetime":"1581633724548"} -{"geo_location":{"latitude":48.17130982924529,"longitude":11.563208418153053},"datetime":"1581633730535"} -{"geo_location":{"latitude":48.17130694149507,"longitude":11.56320032456987},"datetime":"1581633736552"} -{"geo_location":{"latitude":48.17132068611235,"longitude":11.563178822329546},"datetime":"1581633742547"} -{"geo_location":{"latitude":48.17132463655833,"longitude":11.563177082885625},"datetime":"1581633748554"} -{"geo_location":{"latitude":48.17131253315505,"longitude":11.56319005365194},"datetime":"1581633754538"} -{"geo_location":{"latitude":48.17131800130947,"longitude":11.563182633985699},"datetime":"1581633760552"} -{"geo_location":{"latitude":48.17131755621913,"longitude":11.563167472268956},"datetime":"1581633766560"} -{"geo_location":{"latitude":48.17131626593944,"longitude":11.563175580431988},"datetime":"1581633772555"} -{"geo_location":{"latitude":48.17129311805048,"longitude":11.563180197355466},"datetime":"1581633778547"} -{"geo_location":{"latitude":48.17129965259196,"longitude":11.563202551545556},"datetime":"1581633784560"} -{"geo_location":{"latitude":48.17130379171522,"longitude":11.563161991515006},"datetime":"1581633790544"} -{"geo_location":{"latitude":48.17130874979778,"longitude":11.563167560772891},"datetime":"1581633796537"} -{"geo_location":{"latitude":48.17129987346022,"longitude":11.563195516162514},"datetime":"1581633802559"} -{"geo_location":{"latitude":48.17130405252229,"longitude":11.563196101547113},"datetime":"1581633808550"} -{"geo_location":{"latitude":48.171298874794,"longitude":11.563190548943389},"datetime":"1581633814652"} -{"geo_location":{"latitude":48.171317882076316,"longitude":11.563186840799217},"datetime":"1581633820551"} -{"geo_location":{"latitude":48.171309370106414,"longitude":11.563195014202554},"datetime":"1581633826557"} -{"geo_location":{"latitude":48.17130828020122,"longitude":11.563180397841093},"datetime":"1581633832559"} -{"geo_location":{"latitude":48.17131014995013,"longitude":11.563192693335825},"datetime":"1581633838542"} -{"geo_location":{"latitude":48.171304562802966,"longitude":11.563215941762108},"datetime":"1581633844548"} diff --git a/packages/mobility_features/test/testdata/moves.json b/packages/mobility_features/test/testdata/moves.json index 0f2ba4cc3..679fd2845 100644 --- a/packages/mobility_features/test/testdata/moves.json +++ b/packages/mobility_features/test/testdata/moves.json @@ -1,25 +1,25 @@ -{"stop_from":{"geo_location":{"latitude":48.171386612651546,"longitude":11.563414930789028},"place_id":0,"arrival":1581548434570,"departure":1581573471000},"stop_to":{"geo_location":{"latitude":48.15180786902203,"longitude":11.570675076139496},"place_id":1,"arrival":1581574208000,"departure":1581574286000},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.15180786902203,"longitude":11.570675076139496},"place_id":1,"arrival":1581574208000,"departure":1581574286000},"stop_to":{"geo_location":{"latitude":48.1415814591214,"longitude":11.568183708822179},"place_id":2,"arrival":1581574887999,"departure":1581579064202},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.1415814591214,"longitude":11.568183708822179},"place_id":2,"arrival":1581574887999,"departure":1581579064202},"stop_to":{"geo_location":{"latitude":48.1504296953681,"longitude":11.566743217383225},"place_id":3,"arrival":1581579777999,"departure":1581579798999},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.1504296953681,"longitude":11.566743217383225},"place_id":3,"arrival":1581579777999,"departure":1581579798999},"stop_to":{"geo_location":{"latitude":48.16787021104484,"longitude":11.565147249278777},"place_id":4,"arrival":1581580295999,"departure":1581580356999},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.16787021104484,"longitude":11.565147249278777},"place_id":4,"arrival":1581580295999,"departure":1581580356999},"stop_to":{"geo_location":{"latitude":48.17140848487236,"longitude":11.563477734901618},"place_id":0,"arrival":1581580476999,"departure":1581581492999},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.17140848487236,"longitude":11.563477734901618},"place_id":0,"arrival":1581580476999,"departure":1581581492999},"stop_to":{"geo_location":{"latitude":48.166493289434975,"longitude":11.590099831774715},"place_id":5,"arrival":1581581964999,"departure":1581581986999},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.166493289434975,"longitude":11.590099831774715},"place_id":5,"arrival":1581581964999,"departure":1581581986999},"stop_to":{"geo_location":{"latitude":48.21135811752502,"longitude":11.61641499268229},"place_id":6,"arrival":1581582720999,"departure":1581582748999},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.21135811752502,"longitude":11.61641499268229},"place_id":6,"arrival":1581582720999,"departure":1581582748999},"stop_to":{"geo_location":{"latitude":48.26505202402048,"longitude":11.651737746005226},"place_id":7,"arrival":1581583114155,"departure":1581583165198},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.26505202402048,"longitude":11.651737746005226},"place_id":7,"arrival":1581583114155,"departure":1581583165198},"stop_to":{"geo_location":{"latitude":48.26254938066689,"longitude":11.668640250356418},"place_id":8,"arrival":1581583481000,"departure":1581583508999},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.26254938066689,"longitude":11.668640250356418},"place_id":8,"arrival":1581583481000,"departure":1581583508999},"stop_to":{"geo_location":{"latitude":48.26257763760641,"longitude":11.666777112529939},"place_id":9,"arrival":1581583614000,"departure":1581583903404},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.26257763760641,"longitude":11.666777112529939},"place_id":9,"arrival":1581583614000,"departure":1581583903404},"stop_to":{"geo_location":{"latitude":48.262125438156204,"longitude":11.668677326491046},"place_id":8,"arrival":1581585049309,"departure":1581585159252},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.262125438156204,"longitude":11.668677326491046},"place_id":8,"arrival":1581585049309,"departure":1581585159252},"stop_to":{"geo_location":{"latitude":48.26277057065003,"longitude":11.666868257639507},"place_id":9,"arrival":1581585262142,"departure":1581587784367},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.26277057065003,"longitude":11.666868257639507},"place_id":9,"arrival":1581585262142,"departure":1581587784367},"stop_to":{"geo_location":{"latitude":48.268050906046824,"longitude":11.671886509382954},"place_id":10,"arrival":1581588985438,"departure":1581590752024},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.268050906046824,"longitude":11.671886509382954},"place_id":10,"arrival":1581588985438,"departure":1581590752024},"stop_to":{"geo_location":{"latitude":48.26233590463306,"longitude":11.668574732199161},"place_id":8,"arrival":1581591495000,"departure":1581593240999},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.26233590463306,"longitude":11.668574732199161},"place_id":8,"arrival":1581591495000,"departure":1581593240999},"stop_to":{"geo_location":{"latitude":48.26286749915574,"longitude":11.666522133371},"place_id":9,"arrival":1581593402134,"departure":1581596197316},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.26286749915574,"longitude":11.666522133371},"place_id":9,"arrival":1581593402134,"departure":1581596197316},"stop_to":{"geo_location":{"latitude":48.262504482026245,"longitude":11.668389434158957},"place_id":8,"arrival":1581596392804,"departure":1581596448469},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.262504482026245,"longitude":11.668389434158957},"place_id":8,"arrival":1581596392804,"departure":1581596448469},"stop_to":{"geo_location":{"latitude":48.26286828553107,"longitude":11.666566430605975},"place_id":9,"arrival":1581596662684,"departure":1581599426719},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.26286828553107,"longitude":11.666566430605975},"place_id":9,"arrival":1581596662684,"departure":1581599426719},"stop_to":{"geo_location":{"latitude":48.26258553968282,"longitude":11.667927814298238},"place_id":8,"arrival":1581599829447,"departure":1581600214446},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.26258553968282,"longitude":11.667927814298238},"place_id":8,"arrival":1581599829447,"departure":1581600214446},"stop_to":{"geo_location":{"latitude":48.262844228101784,"longitude":11.666359408625585},"place_id":9,"arrival":1581600840883,"departure":1581606295366},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.262844228101784,"longitude":11.666359408625585},"place_id":9,"arrival":1581600840883,"departure":1581606295366},"stop_to":{"geo_location":{"latitude":48.262580403192686,"longitude":11.6678265961852},"place_id":8,"arrival":1581606502755,"departure":1581606803823},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.262580403192686,"longitude":11.6678265961852},"place_id":8,"arrival":1581606502755,"departure":1581606803823},"stop_to":{"geo_location":{"latitude":48.211442262912854,"longitude":11.616452106516075},"place_id":6,"arrival":1581607480000,"departure":1581607504000},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.211442262912854,"longitude":11.616452106516075},"place_id":6,"arrival":1581607480000,"departure":1581607504000},"stop_to":{"geo_location":{"latitude":48.19160641714296,"longitude":11.614063258104917},"place_id":11,"arrival":1581607722000,"departure":1581607755999},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.19160641714296,"longitude":11.614063258104917},"place_id":11,"arrival":1581607722000,"departure":1581607755999},"stop_to":{"geo_location":{"latitude":48.16663433495479,"longitude":11.590159019361},"place_id":5,"arrival":1581608176000,"departure":1581608202000},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.16663433495479,"longitude":11.590159019361},"place_id":5,"arrival":1581608176000,"departure":1581608202000},"stop_to":{"geo_location":{"latitude":48.1668372949315,"longitude":11.5782162847473},"place_id":12,"arrival":1581608423999,"departure":1581608702999},"distance":0.0} -{"stop_from":{"geo_location":{"latitude":48.1668372949315,"longitude":11.5782162847473},"place_id":12,"arrival":1581608423999,"departure":1581608702999},"stop_to":{"geo_location":{"latitude":48.1713134906651,"longitude":11.56320208489327},"place_id":0,"arrival":1581609221143,"departure":1581633141553},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.171386612651546,"longitude":11.563414930789028},"placeId":0,"arrival":"2020-02-13T00:00:34.570","departure":"2020-02-13T06:57:51.000"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.15180786902203,"longitude":11.570675076139496},"placeId":1,"arrival":"2020-02-13T07:10:08.000","departure":"2020-02-13T07:11:26.000"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.15180786902203,"longitude":11.570675076139496},"placeId":1,"arrival":"2020-02-13T07:10:08.000","departure":"2020-02-13T07:11:26.000"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.1415814591214,"longitude":11.568183708822179},"placeId":2,"arrival":"2020-02-13T07:21:27.999","departure":"2020-02-13T08:31:04.202"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.1415814591214,"longitude":11.568183708822179},"placeId":2,"arrival":"2020-02-13T07:21:27.999","departure":"2020-02-13T08:31:04.202"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.1504296953681,"longitude":11.566743217383225},"placeId":3,"arrival":"2020-02-13T08:42:57.999","departure":"2020-02-13T08:43:18.999"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.1504296953681,"longitude":11.566743217383225},"placeId":3,"arrival":"2020-02-13T08:42:57.999","departure":"2020-02-13T08:43:18.999"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.16787021104484,"longitude":11.565147249278777},"placeId":4,"arrival":"2020-02-13T08:51:35.999","departure":"2020-02-13T08:52:36.999"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.16787021104484,"longitude":11.565147249278777},"placeId":4,"arrival":"2020-02-13T08:51:35.999","departure":"2020-02-13T08:52:36.999"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.17140848487236,"longitude":11.563477734901618},"placeId":0,"arrival":"2020-02-13T08:54:36.999","departure":"2020-02-13T09:11:32.999"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.17140848487236,"longitude":11.563477734901618},"placeId":0,"arrival":"2020-02-13T08:54:36.999","departure":"2020-02-13T09:11:32.999"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.166493289434975,"longitude":11.590099831774715},"placeId":5,"arrival":"2020-02-13T09:19:24.999","departure":"2020-02-13T09:19:46.999"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.166493289434975,"longitude":11.590099831774715},"placeId":5,"arrival":"2020-02-13T09:19:24.999","departure":"2020-02-13T09:19:46.999"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.21135811752502,"longitude":11.61641499268229},"placeId":6,"arrival":"2020-02-13T09:32:00.999","departure":"2020-02-13T09:32:28.999"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.21135811752502,"longitude":11.61641499268229},"placeId":6,"arrival":"2020-02-13T09:32:00.999","departure":"2020-02-13T09:32:28.999"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26505202402048,"longitude":11.651737746005226},"placeId":7,"arrival":"2020-02-13T09:38:34.155","departure":"2020-02-13T09:39:25.198"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26505202402048,"longitude":11.651737746005226},"placeId":7,"arrival":"2020-02-13T09:38:34.155","departure":"2020-02-13T09:39:25.198"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26254938066689,"longitude":11.668640250356418},"placeId":8,"arrival":"2020-02-13T09:44:41.000","departure":"2020-02-13T09:45:08.999"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26254938066689,"longitude":11.668640250356418},"placeId":8,"arrival":"2020-02-13T09:44:41.000","departure":"2020-02-13T09:45:08.999"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26257763760641,"longitude":11.666777112529939},"placeId":9,"arrival":"2020-02-13T09:46:54.000","departure":"2020-02-13T09:51:43.404"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26257763760641,"longitude":11.666777112529939},"placeId":9,"arrival":"2020-02-13T09:46:54.000","departure":"2020-02-13T09:51:43.404"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.262125438156204,"longitude":11.668677326491046},"placeId":8,"arrival":"2020-02-13T10:10:49.309","departure":"2020-02-13T10:12:39.252"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.262125438156204,"longitude":11.668677326491046},"placeId":8,"arrival":"2020-02-13T10:10:49.309","departure":"2020-02-13T10:12:39.252"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26277057065003,"longitude":11.666868257639507},"placeId":9,"arrival":"2020-02-13T10:14:22.142","departure":"2020-02-13T10:56:24.367"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26277057065003,"longitude":11.666868257639507},"placeId":9,"arrival":"2020-02-13T10:14:22.142","departure":"2020-02-13T10:56:24.367"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.268050906046824,"longitude":11.671886509382954},"placeId":10,"arrival":"2020-02-13T11:16:25.438","departure":"2020-02-13T11:45:52.024"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.268050906046824,"longitude":11.671886509382954},"placeId":10,"arrival":"2020-02-13T11:16:25.438","departure":"2020-02-13T11:45:52.024"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26233590463306,"longitude":11.668574732199161},"placeId":8,"arrival":"2020-02-13T11:58:15.000","departure":"2020-02-13T12:27:20.999"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26233590463306,"longitude":11.668574732199161},"placeId":8,"arrival":"2020-02-13T11:58:15.000","departure":"2020-02-13T12:27:20.999"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26286749915574,"longitude":11.666522133371},"placeId":9,"arrival":"2020-02-13T12:30:02.134","departure":"2020-02-13T13:16:37.316"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26286749915574,"longitude":11.666522133371},"placeId":9,"arrival":"2020-02-13T12:30:02.134","departure":"2020-02-13T13:16:37.316"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.262504482026245,"longitude":11.668389434158957},"placeId":8,"arrival":"2020-02-13T13:19:52.804","departure":"2020-02-13T13:20:48.469"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.262504482026245,"longitude":11.668389434158957},"placeId":8,"arrival":"2020-02-13T13:19:52.804","departure":"2020-02-13T13:20:48.469"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26286828553107,"longitude":11.666566430605975},"placeId":9,"arrival":"2020-02-13T13:24:22.684","departure":"2020-02-13T14:10:26.719"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26286828553107,"longitude":11.666566430605975},"placeId":9,"arrival":"2020-02-13T13:24:22.684","departure":"2020-02-13T14:10:26.719"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26258553968282,"longitude":11.667927814298238},"placeId":8,"arrival":"2020-02-13T14:17:09.447","departure":"2020-02-13T14:23:34.446"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26258553968282,"longitude":11.667927814298238},"placeId":8,"arrival":"2020-02-13T14:17:09.447","departure":"2020-02-13T14:23:34.446"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.262844228101784,"longitude":11.666359408625585},"placeId":9,"arrival":"2020-02-13T14:34:00.883","departure":"2020-02-13T16:04:55.366"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.262844228101784,"longitude":11.666359408625585},"placeId":9,"arrival":"2020-02-13T14:34:00.883","departure":"2020-02-13T16:04:55.366"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.262580403192686,"longitude":11.6678265961852},"placeId":8,"arrival":"2020-02-13T16:08:22.755","departure":"2020-02-13T16:13:23.823"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.262580403192686,"longitude":11.6678265961852},"placeId":8,"arrival":"2020-02-13T16:08:22.755","departure":"2020-02-13T16:13:23.823"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.211442262912854,"longitude":11.616452106516075},"placeId":6,"arrival":"2020-02-13T16:24:40.000","departure":"2020-02-13T16:25:04.000"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.211442262912854,"longitude":11.616452106516075},"placeId":6,"arrival":"2020-02-13T16:24:40.000","departure":"2020-02-13T16:25:04.000"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.19160641714296,"longitude":11.614063258104917},"placeId":11,"arrival":"2020-02-13T16:28:42.000","departure":"2020-02-13T16:29:15.999"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.19160641714296,"longitude":11.614063258104917},"placeId":11,"arrival":"2020-02-13T16:28:42.000","departure":"2020-02-13T16:29:15.999"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.16663433495479,"longitude":11.590159019361},"placeId":5,"arrival":"2020-02-13T16:36:16.000","departure":"2020-02-13T16:36:42.000"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.16663433495479,"longitude":11.590159019361},"placeId":5,"arrival":"2020-02-13T16:36:16.000","departure":"2020-02-13T16:36:42.000"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.1668372949315,"longitude":11.5782162847473},"placeId":12,"arrival":"2020-02-13T16:40:23.999","departure":"2020-02-13T16:45:02.999"},"distance":0.0} +{"__type":"Move","stopFrom":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.1668372949315,"longitude":11.5782162847473},"placeId":12,"arrival":"2020-02-13T16:40:23.999","departure":"2020-02-13T16:45:02.999"},"stopTo":{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.1713134906651,"longitude":11.56320208489327},"placeId":0,"arrival":"2020-02-13T16:53:41.143","departure":"2020-02-13T23:32:21.553"},"distance":0.0} diff --git a/packages/mobility_features/test/testdata/stops.json b/packages/mobility_features/test/testdata/stops.json index c8d9d23cd..8a59ba3ac 100644 --- a/packages/mobility_features/test/testdata/stops.json +++ b/packages/mobility_features/test/testdata/stops.json @@ -1,26 +1,26 @@ -{"geo_location":{"latitude":48.171386612651546,"longitude":11.563414930789028},"place_id":0,"arrival":1581548434570,"departure":1581573471000} -{"geo_location":{"latitude":48.15180786902203,"longitude":11.570675076139496},"place_id":1,"arrival":1581574208000,"departure":1581574286000} -{"geo_location":{"latitude":48.1415814591214,"longitude":11.568183708822179},"place_id":2,"arrival":1581574887999,"departure":1581579064202} -{"geo_location":{"latitude":48.1504296953681,"longitude":11.566743217383225},"place_id":3,"arrival":1581579777999,"departure":1581579798999} -{"geo_location":{"latitude":48.16787021104484,"longitude":11.565147249278777},"place_id":4,"arrival":1581580295999,"departure":1581580356999} -{"geo_location":{"latitude":48.17140848487236,"longitude":11.563477734901618},"place_id":0,"arrival":1581580476999,"departure":1581581492999} -{"geo_location":{"latitude":48.166493289434975,"longitude":11.590099831774715},"place_id":5,"arrival":1581581964999,"departure":1581581986999} -{"geo_location":{"latitude":48.21135811752502,"longitude":11.61641499268229},"place_id":6,"arrival":1581582720999,"departure":1581582748999} -{"geo_location":{"latitude":48.26505202402048,"longitude":11.651737746005226},"place_id":7,"arrival":1581583114155,"departure":1581583165198} -{"geo_location":{"latitude":48.26254938066689,"longitude":11.668640250356418},"place_id":8,"arrival":1581583481000,"departure":1581583508999} -{"geo_location":{"latitude":48.26257763760641,"longitude":11.666777112529939},"place_id":9,"arrival":1581583614000,"departure":1581583903404} -{"geo_location":{"latitude":48.262125438156204,"longitude":11.668677326491046},"place_id":8,"arrival":1581585049309,"departure":1581585159252} -{"geo_location":{"latitude":48.26277057065003,"longitude":11.666868257639507},"place_id":9,"arrival":1581585262142,"departure":1581587784367} -{"geo_location":{"latitude":48.268050906046824,"longitude":11.671886509382954},"place_id":10,"arrival":1581588985438,"departure":1581590752024} -{"geo_location":{"latitude":48.26233590463306,"longitude":11.668574732199161},"place_id":8,"arrival":1581591495000,"departure":1581593240999} -{"geo_location":{"latitude":48.26286749915574,"longitude":11.666522133371},"place_id":9,"arrival":1581593402134,"departure":1581596197316} -{"geo_location":{"latitude":48.262504482026245,"longitude":11.668389434158957},"place_id":8,"arrival":1581596392804,"departure":1581596448469} -{"geo_location":{"latitude":48.26286828553107,"longitude":11.666566430605975},"place_id":9,"arrival":1581596662684,"departure":1581599426719} -{"geo_location":{"latitude":48.26258553968282,"longitude":11.667927814298238},"place_id":8,"arrival":1581599829447,"departure":1581600214446} -{"geo_location":{"latitude":48.262844228101784,"longitude":11.666359408625585},"place_id":9,"arrival":1581600840883,"departure":1581606295366} -{"geo_location":{"latitude":48.262580403192686,"longitude":11.6678265961852},"place_id":8,"arrival":1581606502755,"departure":1581606803823} -{"geo_location":{"latitude":48.211442262912854,"longitude":11.616452106516075},"place_id":6,"arrival":1581607480000,"departure":1581607504000} -{"geo_location":{"latitude":48.19160641714296,"longitude":11.614063258104917},"place_id":11,"arrival":1581607722000,"departure":1581607755999} -{"geo_location":{"latitude":48.16663433495479,"longitude":11.590159019361},"place_id":5,"arrival":1581608176000,"departure":1581608202000} -{"geo_location":{"latitude":48.1668372949315,"longitude":11.5782162847473},"place_id":12,"arrival":1581608423999,"departure":1581608702999} -{"geo_location":{"latitude":48.1713134906651,"longitude":11.56320208489327},"place_id":0,"arrival":1581609221143,"departure":1581633141553} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.171386612651546,"longitude":11.563414930789028},"placeId":0,"arrival":"2020-02-13T00:00:34.570","departure":"2020-02-13T06:57:51.000"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.15180786902203,"longitude":11.570675076139496},"placeId":1,"arrival":"2020-02-13T07:10:08.000","departure":"2020-02-13T07:11:26.000"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.1415814591214,"longitude":11.568183708822179},"placeId":2,"arrival":"2020-02-13T07:21:27.999","departure":"2020-02-13T08:31:04.202"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.1504296953681,"longitude":11.566743217383225},"placeId":3,"arrival":"2020-02-13T08:42:57.999","departure":"2020-02-13T08:43:18.999"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.16787021104484,"longitude":11.565147249278777},"placeId":4,"arrival":"2020-02-13T08:51:35.999","departure":"2020-02-13T08:52:36.999"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.17140848487236,"longitude":11.563477734901618},"placeId":0,"arrival":"2020-02-13T08:54:36.999","departure":"2020-02-13T09:11:32.999"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.166493289434975,"longitude":11.590099831774715},"placeId":5,"arrival":"2020-02-13T09:19:24.999","departure":"2020-02-13T09:19:46.999"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.21135811752502,"longitude":11.61641499268229},"placeId":6,"arrival":"2020-02-13T09:32:00.999","departure":"2020-02-13T09:32:28.999"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26505202402048,"longitude":11.651737746005226},"placeId":7,"arrival":"2020-02-13T09:38:34.155","departure":"2020-02-13T09:39:25.198"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26254938066689,"longitude":11.668640250356418},"placeId":8,"arrival":"2020-02-13T09:44:41.000","departure":"2020-02-13T09:45:08.999"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26257763760641,"longitude":11.666777112529939},"placeId":9,"arrival":"2020-02-13T09:46:54.000","departure":"2020-02-13T09:51:43.404"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.262125438156204,"longitude":11.668677326491046},"placeId":8,"arrival":"2020-02-13T10:10:49.309","departure":"2020-02-13T10:12:39.252"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26277057065003,"longitude":11.666868257639507},"placeId":9,"arrival":"2020-02-13T10:14:22.142","departure":"2020-02-13T10:56:24.367"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.268050906046824,"longitude":11.671886509382954},"placeId":10,"arrival":"2020-02-13T11:16:25.438","departure":"2020-02-13T11:45:52.024"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26233590463306,"longitude":11.668574732199161},"placeId":8,"arrival":"2020-02-13T11:58:15.000","departure":"2020-02-13T12:27:20.999"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26286749915574,"longitude":11.666522133371},"placeId":9,"arrival":"2020-02-13T12:30:02.134","departure":"2020-02-13T13:16:37.316"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.262504482026245,"longitude":11.668389434158957},"placeId":8,"arrival":"2020-02-13T13:19:52.804","departure":"2020-02-13T13:20:48.469"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26286828553107,"longitude":11.666566430605975},"placeId":9,"arrival":"2020-02-13T13:24:22.684","departure":"2020-02-13T14:10:26.719"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.26258553968282,"longitude":11.667927814298238},"placeId":8,"arrival":"2020-02-13T14:17:09.447","departure":"2020-02-13T14:23:34.446"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.262844228101784,"longitude":11.666359408625585},"placeId":9,"arrival":"2020-02-13T14:34:00.883","departure":"2020-02-13T16:04:55.366"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.262580403192686,"longitude":11.6678265961852},"placeId":8,"arrival":"2020-02-13T16:08:22.755","departure":"2020-02-13T16:13:23.823"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.211442262912854,"longitude":11.616452106516075},"placeId":6,"arrival":"2020-02-13T16:24:40.000","departure":"2020-02-13T16:25:04.000"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.19160641714296,"longitude":11.614063258104917},"placeId":11,"arrival":"2020-02-13T16:28:42.000","departure":"2020-02-13T16:29:15.999"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.16663433495479,"longitude":11.590159019361},"placeId":5,"arrival":"2020-02-13T16:36:16.000","departure":"2020-02-13T16:36:42.000"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.1668372949315,"longitude":11.5782162847473},"placeId":12,"arrival":"2020-02-13T16:40:23.999","departure":"2020-02-13T16:45:02.999"} +{"__type":"Stop","geoLocation":{"__type":"GeoLocation","latitude":48.1713134906651,"longitude":11.56320208489327},"placeId":0,"arrival":"2020-02-13T16:53:41.143","departure":"2020-02-13T23:32:21.553"} From 75d0b038ad3d956aaa4a9f22c72bd4acef8c7920 Mon Sep 17 00:00:00 2001 From: Philipp Bauer Date: Sat, 5 Oct 2024 21:57:05 +0200 Subject: [PATCH 37/80] Remove automatically added workout permissions --- .../cachet/plugins/health/HealthPlugin.kt | 64 ------------------- 1 file changed, 64 deletions(-) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 70690126a..15c9a08c3 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -540,38 +540,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( @@ -625,38 +593,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) From 22f35874ca09bb551b31bee83b99a74037450dbe Mon Sep 17 00:00:00 2001 From: iarata <6937697+iarata@users.noreply.github.com> Date: Wed, 30 Oct 2024 13:47:09 +0100 Subject: [PATCH 38/80] Add check flag for permission to prevent multiple requests --- .../example/.flutter-plugins-dependencies | 2 +- .../cachet/plugins/health/HealthPlugin.kt | 23 +++++++++++-------- .../example/.flutter-plugins-dependencies | 2 +- .../ios/Flutter/flutter_export_environment.sh | 4 ++-- .../example/.flutter-plugins-dependencies | 2 +- .../ios/Flutter/flutter_export_environment.sh | 4 ++-- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/packages/app_usage/example/.flutter-plugins-dependencies b/packages/app_usage/example/.flutter-plugins-dependencies index 8869a5276..72bea7d1c 100644 --- a/packages/app_usage/example/.flutter-plugins-dependencies +++ b/packages/app_usage/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[],"android":[{"name":"app_usage","path":"/Users/hoffmatteo/Desktop/CACHET/flutter-plugins/packages/app_usage/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"app_usage","dependencies":[]}],"date_created":"2023-07-20 21:35:52.362642","version":"3.10.5"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[],"android":[{"name":"app_usage","path":"/Users/arata/Developer/carp/flutter-plugins/packages/app_usage/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"app_usage","dependencies":[]}],"date_created":"2024-10-30 13:38:21.146381","version":"3.24.3","swift_package_manager_enabled":false} \ No newline at end of file diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 70690126a..216aff7ea 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -94,6 +94,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : null private lateinit var healthConnectClient: HealthConnectClient private lateinit var scope: CoroutineScope + private var isReplySubmitted = false override fun onAttachedToEngine( @@ -219,15 +220,18 @@ class HealthPlugin(private var channel: MethodChannel? = null) : } private fun onHealthConnectPermissionCallback(permissionGranted: Set) { - if (permissionGranted.isEmpty()) { - mResult?.success(false) - Log.i("FLUTTER_HEALTH", "Health Connect permissions were not granted! Make sure to declare the required permissions in the AndroidManifest.xml file.") - } else { - mResult?.success(true) - Log.i("FLUTTER_HEALTH", "${permissionGranted.size} Health Connect permissions were granted!") - - // log the permissions granted for debugging - Log.i("FLUTTER_HEALTH", "Permissions granted: $permissionGranted") + if (!isReplySubmitted) { + if (permissionGranted.isEmpty()) { + mResult?.success(false) + Log.i("FLUTTER_HEALTH", "Health Connect permissions were not granted! Make sure to declare the required permissions in the AndroidManifest.xml file.") + } else { + mResult?.success(true) + Log.i("FLUTTER_HEALTH", "${permissionGranted.size} Health Connect permissions were granted!") + + // log the permissions granted for debugging + Log.i("FLUTTER_HEALTH", "Permissions granted: $permissionGranted") + } + isReplySubmitted = true } } @@ -666,6 +670,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) : // Store the result to be called in [onHealthConnectPermissionCallback] mResult = result + isReplySubmitted = false healthConnectRequestPermissionsLauncher!!.launch(permList.toSet()) } diff --git a/packages/notifications/example/.flutter-plugins-dependencies b/packages/notifications/example/.flutter-plugins-dependencies index bce6c1b5c..77ab4de4d 100644 --- a/packages/notifications/example/.flutter-plugins-dependencies +++ b/packages/notifications/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"notifications","path":"/Users/bardram/dev/flutter-plugins/packages/notifications/","dependencies":[]}],"android":[{"name":"notifications","path":"/Users/bardram/dev/flutter-plugins/packages/notifications/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"notifications","dependencies":[]}],"date_created":"2021-07-22 09:08:42.769208","version":"2.2.3"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"notifications","path":"/Users/arata/Developer/carp/flutter-plugins/packages/notifications/","native_build":true,"dependencies":[]}],"android":[{"name":"notifications","path":"/Users/arata/Developer/carp/flutter-plugins/packages/notifications/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"notifications","dependencies":[]}],"date_created":"2024-10-30 13:38:55.333244","version":"3.24.3","swift_package_manager_enabled":false} \ No newline at end of file diff --git a/packages/notifications/example/ios/Flutter/flutter_export_environment.sh b/packages/notifications/example/ios/Flutter/flutter_export_environment.sh index 0d97f7bb4..886abaadb 100755 --- a/packages/notifications/example/ios/Flutter/flutter_export_environment.sh +++ b/packages/notifications/example/ios/Flutter/flutter_export_environment.sh @@ -1,7 +1,7 @@ #!/bin/sh # This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=/Users/hoffmatteo/dev/flutter" -export "FLUTTER_APPLICATION_PATH=/Users/hoffmatteo/Desktop/CACHET/flutter-plugins/packages/notifications/example" +export "FLUTTER_ROOT=/opt/homebrew/Caskroom/flutter/3.24.3/flutter" +export "FLUTTER_APPLICATION_PATH=/Users/arata/Developer/carp/flutter-plugins/packages/notifications/example" export "COCOAPODS_PARALLEL_CODE_SIGN=true" export "FLUTTER_TARGET=lib/main.dart" export "FLUTTER_BUILD_DIR=build" diff --git a/packages/pedometer/example/.flutter-plugins-dependencies b/packages/pedometer/example/.flutter-plugins-dependencies index a50559c92..8c805c999 100644 --- a/packages/pedometer/example/.flutter-plugins-dependencies +++ b/packages/pedometer/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"pedometer","path":"/Users/bardram/dev/flutter-plugins/packages/pedometer/","native_build":true,"dependencies":[]},{"name":"permission_handler_apple","path":"/Users/bardram/.pub-cache/hosted/pub.dev/permission_handler_apple-9.4.5/","native_build":true,"dependencies":[]}],"android":[{"name":"pedometer","path":"/Users/bardram/dev/flutter-plugins/packages/pedometer/","native_build":true,"dependencies":[]},{"name":"permission_handler_android","path":"/Users/bardram/.pub-cache/hosted/pub.dev/permission_handler_android-12.0.7/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[{"name":"permission_handler_windows","path":"/Users/bardram/.pub-cache/hosted/pub.dev/permission_handler_windows-0.2.1/","native_build":true,"dependencies":[]}],"web":[{"name":"permission_handler_html","path":"/Users/bardram/.pub-cache/hosted/pub.dev/permission_handler_html-0.1.2/","dependencies":[]}]},"dependencyGraph":[{"name":"pedometer","dependencies":[]},{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_html","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_html","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]}],"date_created":"2024-07-31 13:12:00.381228","version":"3.22.2"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"pedometer","path":"/Users/arata/Developer/carp/flutter-plugins/packages/pedometer/","native_build":true,"dependencies":[]},{"name":"permission_handler_apple","path":"/Users/arata/.pub-cache/hosted/pub.dev/permission_handler_apple-9.4.5/","native_build":true,"dependencies":[]}],"android":[{"name":"pedometer","path":"/Users/arata/Developer/carp/flutter-plugins/packages/pedometer/","native_build":true,"dependencies":[]},{"name":"permission_handler_android","path":"/Users/arata/.pub-cache/hosted/pub.dev/permission_handler_android-12.0.13/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[{"name":"permission_handler_windows","path":"/Users/arata/.pub-cache/hosted/pub.dev/permission_handler_windows-0.2.1/","native_build":true,"dependencies":[]}],"web":[{"name":"permission_handler_html","path":"/Users/arata/.pub-cache/hosted/pub.dev/permission_handler_html-0.1.3+2/","dependencies":[]}]},"dependencyGraph":[{"name":"pedometer","dependencies":[]},{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_html","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_html","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]}],"date_created":"2024-10-30 13:38:57.617363","version":"3.24.3","swift_package_manager_enabled":false} \ No newline at end of file diff --git a/packages/pedometer/example/ios/Flutter/flutter_export_environment.sh b/packages/pedometer/example/ios/Flutter/flutter_export_environment.sh index be27e1299..4325b283e 100755 --- a/packages/pedometer/example/ios/Flutter/flutter_export_environment.sh +++ b/packages/pedometer/example/ios/Flutter/flutter_export_environment.sh @@ -1,7 +1,7 @@ #!/bin/sh # This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=/Users/bardram/dev/flutter" -export "FLUTTER_APPLICATION_PATH=/Users/bardram/dev/flutter-plugins/packages/pedometer/example" +export "FLUTTER_ROOT=/opt/homebrew/Caskroom/flutter/3.24.3/flutter" +export "FLUTTER_APPLICATION_PATH=/Users/arata/Developer/carp/flutter-plugins/packages/pedometer/example" export "COCOAPODS_PARALLEL_CODE_SIGN=true" export "FLUTTER_TARGET=lib/main.dart" export "FLUTTER_BUILD_DIR=build" From 09a55e409e285c73e346ddf8c25c8de95bc4fa2b Mon Sep 17 00:00:00 2001 From: iarata <6937697+iarata@users.noreply.github.com> Date: Wed, 30 Oct 2024 13:49:03 +0100 Subject: [PATCH 39/80] Bump version to 11.1.1 and update CHANGELOG for issue #1059 fix --- packages/health/CHANGELOG.md | 4 ++++ packages/health/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index a85a38e57..d1e668fd7 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,3 +1,7 @@ +## 11.1.1 + +* Fix of [#1059](https://github.com/cph-cachet/flutter-plugins/issues/1059) + ## 11.1.0 * Fix of [#1043](https://github.com/cph-cachet/flutter-plugins/issues/1043) diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index 7690d1c50..a97aeaa6f 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -1,6 +1,6 @@ name: health description: Wrapper for Apple's HealthKit on iOS and Google's Health Connect on Android. -version: 11.1.0 +version: 11.1.1 homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages/health environment: From bf4298032372e1c1a6ca38d4f887fc4d98b47ae1 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Wed, 6 Nov 2024 19:23:41 +0100 Subject: [PATCH 40/80] Replace issue template with issue forms (#1070) * Add bug fix issue template for improved reporting * Disable blank issue creation in the issue template configuration * Update bug fix issue template title format for better clarity --- .github/ISSUE_TEMPLATE/bugfix.yml | 136 ++++++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 1 + 2 files changed, 137 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bugfix.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/bugfix.yml b/.github/ISSUE_TEMPLATE/bugfix.yml new file mode 100644 index 000000000..462d9db86 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bugfix.yml @@ -0,0 +1,136 @@ +name: 🐞 Bug fix +description: Create a report to help us improve any bug you may experience. +title: "[ ] a brief, descriptive title for the bug" +labels: ["bugfix"] +assignees: [] +body: + - type: markdown + attributes: + value: | + ## Bug Report Details + Please fill in the information below to help us understand and fix the bug. + + - type: dropdown + id: plugin + attributes: + label: Plugin Name + description: Select the plugin that has the bug + options: + - screen_state + - light + - pedometer + - noise_meter + - app_usage + - weather + - air_quality + - notifications + - movisens_flutter + - esense_flutter + - health + - activity_recognition + - audio_streamer + - mobility_features + - carp_background_location + - flutter_foreground_service + validations: + required: true + + - type: input + id: version + attributes: + label: Plugin Version + description: What version of the plugin are you using? + placeholder: "e.g. 2.4.1" + validations: + required: true + + - type: markdown + attributes: + value: | + ### Device / Emulator and OS Information + Please provide information for each device/emulator where you experience this bug. + + **Note:** + - For iOS, only the latest OS is supported + - For Android, see [the OS versions for which Google support security fixes](https://en.wikipedia.org/wiki/Android_version_history) + - Bugs pertaining to old devices/OS versions will likely not be fixed. + + - type: input + id: device + attributes: + label: Device + description: What device are you using? + placeholder: "e.g. iPhone 6s" + validations: + required: true + + - type: input + id: os + attributes: + label: Operating System + description: What OS version is installed? + placeholder: "e.g. iOS 13.1" + validations: + required: true + + - type: textarea + id: bug-description + attributes: + label: Describe the bug + description: A clear and concise description of what the bug is. + validations: + required: true + + - type: textarea + id: reproduction-steps + attributes: + label: Steps to Reproduce + description: How can we reproduce this issue? + placeholder: | + 1. Go to '...' + 2. Click on '....' + 3. Scroll down to '....' + 4. See error + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: Expected Behavior + description: What did you expect to happen? + validations: + required: true + + - type: textarea + id: actual-behavior + attributes: + label: Actual Behavior + description: What actually happened? Include stack traces and exception print-outs. + validations: + required: true + + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: If applicable, add screenshots to help explain your problem. + validations: + required: false + + - type: textarea + id: flutter-doctor + attributes: + label: Flutter Doctor Output + description: Please run `flutter doctor` and paste the output here. + render: shell + validations: + required: true + + - type: textarea + id: additional-info + attributes: + label: Additional Information + description: Add any other relevant information about the problem here. + validations: + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..ec4bb386b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false \ No newline at end of file From d85f7d0f155a8a0ffa859bab32ccb32a64260216 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Thu, 7 Nov 2024 04:08:18 +0900 Subject: [PATCH 41/80] Delete .github/ISSUE_TEMPLATE/bugfix.md --- .github/ISSUE_TEMPLATE/bugfix.md | 40 -------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bugfix.md diff --git a/.github/ISSUE_TEMPLATE/bugfix.md b/.github/ISSUE_TEMPLATE/bugfix.md deleted file mode 100644 index 6edbee250..000000000 --- a/.github/ISSUE_TEMPLATE/bugfix.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: Bug fix -about: Create a report to help us improve any bug you may experience. -title: '[Specify the plugin name and version here]' -labels: 'bugfix' -assignees: '' - ---- - -**Remember to specify the plugin name in the title!** - -### Device / Emulator and OS -Please complete the following information for each phone and/or emulator you're experiencing this bug on: - - Device: [e.g. iPhone 6s] - - OS: [e.g. iOS 13.1] - -**NB: Bugs pertaining to old devices/OS versions will likely not be fixed.** -* For iOS only the latest OS is supported. -* For Android, see [the OS versions for which Google support security fixes](https://en.wikipedia.org/wiki/Android_version_history)** - -### Describe the bug -A clear and concise description of what the bug is. - -### To Reproduce -Steps to reproduce the behavior. - -### Expected behavior -What did you expect to happen? - -### Actual behavior -What did happen? Include stack traces and exception print-outs. - -### Screenshots -If applicable, add screenshots to help explain your problem. - -### Flutter doctor -Please run `flutter doctor` and add the output here. - -### Additional information -Add any other info about the problem here. From 1f5800046f699b3edbec8a908bfe9b8ce9766309 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Thu, 7 Nov 2024 04:09:50 +0900 Subject: [PATCH 42/80] Update bugfix.yml --- .github/ISSUE_TEMPLATE/bugfix.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bugfix.yml b/.github/ISSUE_TEMPLATE/bugfix.yml index 462d9db86..2e22f6406 100644 --- a/.github/ISSUE_TEMPLATE/bugfix.yml +++ b/.github/ISSUE_TEMPLATE/bugfix.yml @@ -109,6 +109,14 @@ body: description: What actually happened? Include stack traces and exception print-outs. validations: required: true + + - type: textarea + id: flutter-logs + attributes: + label: Flutter Logs + description: Add flutter logs in here. + validations: + required: false - type: textarea id: screenshots @@ -133,4 +141,4 @@ body: label: Additional Information description: Add any other relevant information about the problem here. validations: - required: false \ No newline at end of file + required: false From 0ea4e7bf9200bc476e8c4db5f75b291341e11a24 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Thu, 7 Nov 2024 04:11:12 +0900 Subject: [PATCH 43/80] Update bugfix.yml --- .github/ISSUE_TEMPLATE/bugfix.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/bugfix.yml b/.github/ISSUE_TEMPLATE/bugfix.yml index 2e22f6406..93704b406 100644 --- a/.github/ISSUE_TEMPLATE/bugfix.yml +++ b/.github/ISSUE_TEMPLATE/bugfix.yml @@ -115,6 +115,7 @@ body: attributes: label: Flutter Logs description: Add flutter logs in here. + render: shell validations: required: false From 2009d03434fbb541b0b06b64bb9b5892b66c64f5 Mon Sep 17 00:00:00 2001 From: Slava Ryabinin Date: Sat, 23 Nov 2024 08:51:31 +0300 Subject: [PATCH 44/80] Fix `HealthDataType was not aligned correctly` when adding SLEEP_LIGHT data --- packages/health/lib/src/health_plugin.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 8868a519b..9223782b9 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -1104,7 +1104,7 @@ class Health { HealthDataType.SLEEP_IN_BED => 0, HealthDataType.SLEEP_ASLEEP => 1, HealthDataType.SLEEP_AWAKE => 2, - HealthDataType.SLEEP_ASLEEP => 3, + HealthDataType.SLEEP_LIGHT => 3, HealthDataType.SLEEP_DEEP => 4, HealthDataType.SLEEP_REM => 5, HealthDataType.HEADACHE_UNSPECIFIED => 0, From 0c6aadf1fc7d190d07d9cd0f1bead463c3d5bc4b Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Mon, 25 Nov 2024 17:39:04 +0100 Subject: [PATCH 45/80] ignore sdkman env --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index f66dbae2d..8bb6a7a81 100644 --- a/.gitignore +++ b/.gitignore @@ -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 \ No newline at end of file From 028484610eed131e0d2675ea2af1dbcf00e341cb Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:47:08 +0100 Subject: [PATCH 46/80] Update AppDelegate.swift to use @main instead of @UIApplicationMain --- packages/health/example/ios/Runner/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/health/example/ios/Runner/AppDelegate.swift b/packages/health/example/ios/Runner/AppDelegate.swift index 70693e4a8..b63630348 100644 --- a/packages/health/example/ios/Runner/AppDelegate.swift +++ b/packages/health/example/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, From bc9f07cf9d57624aa74d66a8d2dc1ac8c3ca91dc Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:33:21 +0100 Subject: [PATCH 47/80] Comment out ECG-related HealthDataTypes in util.dart since iOS will throw error on WRITE --- packages/health/example/lib/util.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/health/example/lib/util.dart b/packages/health/example/lib/util.dart index 0a694b0a2..e1c49101d 100644 --- a/packages/health/example/lib/util.dart +++ b/packages/health/example/lib/util.dart @@ -46,13 +46,13 @@ const List dataTypesIOS = [ HealthDataType.HEADACHE_UNSPECIFIED, // note that a phone cannot write these ECG-based types - only read them - HealthDataType.ELECTROCARDIOGRAM, - HealthDataType.HIGH_HEART_RATE_EVENT, - HealthDataType.IRREGULAR_HEART_RATE_EVENT, - HealthDataType.LOW_HEART_RATE_EVENT, - HealthDataType.RESTING_HEART_RATE, - HealthDataType.WALKING_HEART_RATE, - HealthDataType.ATRIAL_FIBRILLATION_BURDEN, + // HealthDataType.ELECTROCARDIOGRAM, + // HealthDataType.HIGH_HEART_RATE_EVENT, + // HealthDataType.IRREGULAR_HEART_RATE_EVENT, + // HealthDataType.LOW_HEART_RATE_EVENT, + // HealthDataType.RESTING_HEART_RATE, + // HealthDataType.WALKING_HEART_RATE, + // HealthDataType.ATRIAL_FIBRILLATION_BURDEN, HealthDataType.NUTRITION, HealthDataType.GENDER, From 19aa01db47c11f999cc6b5dc968432ab795ef150 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:11:50 +0100 Subject: [PATCH 48/80] Refactor HealthApp widget and improve debug output formatting --- packages/health/example/lib/main.dart | 42 +++++++++++++++------------ 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index db1069ddf..b77eae30e 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -10,6 +10,8 @@ import 'package:carp_serializable/carp_serializable.dart'; void main() => runApp(HealthApp()); class HealthApp extends StatefulWidget { + const HealthApp({super.key}); + @override _HealthAppState createState() => _HealthAppState(); } @@ -175,7 +177,9 @@ class _HealthAppState extends State { // filter out duplicates _healthDataList = Health().removeDuplicates(_healthDataList); - _healthDataList.forEach((data) => debugPrint(toJsonString(data))); + for (var data in _healthDataList) { + debugPrint(toJsonString(data)); + } // update the UI to display the results setState(() { @@ -662,7 +666,7 @@ class _HealthAppState extends State { if (p.value is AudiogramHealthValue) { return ListTile( title: Text("${p.typeString}: ${p.value}"), - trailing: Text('${p.unitString}'), + trailing: Text(p.unitString), subtitle: Text('${p.dateFrom} - ${p.dateTo}\n${p.recordingMethod}'), ); } @@ -671,7 +675,7 @@ class _HealthAppState extends State { title: Text( "${p.typeString}: ${(p.value as WorkoutHealthValue).totalEnergyBurned} ${(p.value as WorkoutHealthValue).totalEnergyBurnedUnit?.name}"), trailing: Text( - '${(p.value as WorkoutHealthValue).workoutActivityType.name}'), + (p.value as WorkoutHealthValue).workoutActivityType.name), subtitle: Text('${p.dateFrom} - ${p.dateTo}\n${p.recordingMethod}'), ); } @@ -686,46 +690,46 @@ class _HealthAppState extends State { } return ListTile( title: Text("${p.typeString}: ${p.value}"), - trailing: Text('${p.unitString}'), + trailing: Text(p.unitString), subtitle: Text('${p.dateFrom} - ${p.dateTo}\n${p.recordingMethod}'), ); }); - Widget _contentNoData = const Text('No Data to show'); + final Widget _contentNoData = const Text('No Data to show'); - Widget _contentNotFetched = + final Widget _contentNotFetched = const Column(mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text("Press 'Auth' to get permissions to access health data."), - const Text("Press 'Fetch Dat' to get health data."), - const Text("Press 'Add Data' to add some random health data."), - const Text("Press 'Delete Data' to remove some random health data."), + Text("Press 'Auth' to get permissions to access health data."), + Text("Press 'Fetch Dat' to get health data."), + Text("Press 'Add Data' to add some random health data."), + Text("Press 'Delete Data' to remove some random health data."), ]); - Widget _authorized = const Text('Authorization granted!'); + final Widget _authorized = const Text('Authorization granted!'); - Widget _authorizationNotGranted = const Column( + final Widget _authorizationNotGranted = const Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text('Authorization not given.'), - const Text( + Text('Authorization not given.'), + Text( 'For Google Health Connect please check if you have added the right permissions and services to the manifest file.'), - const Text('For Apple Health check your permissions in Apple Health.'), + Text('For Apple Health check your permissions in Apple Health.'), ], ); Widget _contentHealthConnectStatus = const Text( 'No status, click getHealthConnectSdkStatus to get the status.'); - Widget _dataAdded = const Text('Data points inserted successfully.'); + final Widget _dataAdded = const Text('Data points inserted successfully.'); - Widget _dataDeleted = const Text('Data points deleted successfully.'); + final Widget _dataDeleted = const Text('Data points deleted successfully.'); Widget get _stepsFetched => Text('Total number of steps: $_nofSteps.'); - Widget _dataNotAdded = + final Widget _dataNotAdded = const Text('Failed to add data.\nDo you have permissions to add data?'); - Widget _dataNotDeleted = const Text('Failed to delete data'); + final Widget _dataNotDeleted = const Text('Failed to delete data'); Widget get _content => switch (_state) { AppState.DATA_READY => _contentDataReady, From 5e39721e2497940d3e70c8baf07af9d49ed26407 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Thu, 28 Nov 2024 15:04:57 +0100 Subject: [PATCH 49/80] Health data query to use HKStatisticsCollectionQuery instead of HKStatisticsQuery --- .../ios/Classes/SwiftHealthPlugin.swift | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index d7af83e05..fda57c54b 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -1171,33 +1171,36 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { predicate = NSCompoundPredicate(type: .and, subpredicates: [predicate, manualPredicate]) } - let query = HKStatisticsQuery( + // TODO: [NOTE] Computational heavy + let query = HKStatisticsCollectionQuery( quantityType: sampleType, quantitySamplePredicate: predicate, - options: .cumulativeSum - ) { query, queryResult, error in - - guard let queryResult = queryResult else { + options: .cumulativeSum, + anchorDate: dateFrom, + intervalComponents: DateComponents(day: 1) + ) + query.initialResultsHandler = { query, results, error in + guard let results = results else { let error = error! as NSError print("Error getting total steps in interval \(error.localizedDescription)") - + DispatchQueue.main.async { result(nil) } return - } - - var steps = 0.0 - - if let quantity = queryResult.sumQuantity() { - let unit = HKUnit.count() - steps = quantity.doubleValue(for: unit) - } - - let totalSteps = Int(steps) - DispatchQueue.main.async { - result(totalSteps) - } + } + + var totalSteps = 0.0 + results.enumerateStatistics(from: dateFrom, to: dateTo) { statistics, stop in + if let quantity = statistics.sumQuantity() { + let unit = HKUnit.count() + totalSteps += quantity.doubleValue(for: unit) + } + } + + DispatchQueue.main.async { + result(Int(totalSteps)) + } } HKHealthStore().execute(query) From c542556f5f32b2651f51c386480ae4c6c06f531c Mon Sep 17 00:00:00 2001 From: Iosif Futerman Date: Wed, 4 Dec 2024 12:01:19 +0200 Subject: [PATCH 50/80] fixed hasPermissions call. If types have permissions result should be true --- packages/health/ios/Classes/SwiftHealthPlugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index d7af83e05..0d7882f87 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -339,7 +339,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } } - result(false) + result(true) } func hasPermission(type: HKObjectType, access: Int) -> Bool? { From cb2601800f03201de009d6aeb061f1c0d032901b Mon Sep 17 00:00:00 2001 From: PeteRyo0517 <> Date: Wed, 11 Dec 2024 14:39:20 +0900 Subject: [PATCH 51/80] Updated device_info_plus version dependency --- packages/health/CHANGELOG.md | 4 ++++ packages/health/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index d1e668fd7..cd792d435 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,3 +1,7 @@ +## 11.1.2 + +* Updated `device_info_plus` version dependency + ## 11.1.1 * Fix of [#1059](https://github.com/cph-cachet/flutter-plugins/issues/1059) diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index a97aeaa6f..54fc3c4bf 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: flutter: sdk: flutter intl: '>=0.18.0 <0.20.0' - device_info_plus: '>=9.0.0 <11.0.0' + device_info_plus: '>=9.0.0 <12.0.0' json_annotation: ^4.8.0 carp_serializable: ^2.0.0 # polymorphic json serialization From 079de1b2bf7761a9ebde8bb568b87b906e11f8fd Mon Sep 17 00:00:00 2001 From: Daniel Cachapa Date: Tue, 12 Nov 2024 16:31:59 +0100 Subject: [PATCH 52/80] [Health] Add lean mass data type Closes #1078 --- packages/health/CHANGELOG.md | 4 ++++ .../cachet/plugins/health/HealthPlugin.kt | 19 ++++++++++++++++++- .../ios/Classes/SwiftHealthPlugin.swift | 3 +++ packages/health/lib/src/heath_data_types.dart | 4 ++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index d1e668fd7..9a417c7ab 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,3 +1,7 @@ +## 11.2.0 + +* Add lean mass data type [#1078](https://github.com/cph-cachet/flutter-plugins/issues/1078) + ## 11.1.1 * Fix of [#1059](https://github.com/cph-cachet/flutter-plugins/issues/1059) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 216aff7ea..23f64dad7 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -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" @@ -1596,6 +1597,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 = @@ -2398,6 +2415,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, @@ -2451,7 +2469,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, diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index d7af83e05..79da70922 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -35,6 +35,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let BLOOD_PRESSURE_DIASTOLIC = "BLOOD_PRESSURE_DIASTOLIC" let BLOOD_PRESSURE_SYSTOLIC = "BLOOD_PRESSURE_SYSTOLIC" let BODY_FAT_PERCENTAGE = "BODY_FAT_PERCENTAGE" + let LEAN_BODY_MASS = "LEAN_BODY_MASS" let BODY_MASS_INDEX = "BODY_MASS_INDEX" let BODY_TEMPERATURE = "BODY_TEMPERATURE" // Nutrition @@ -1409,6 +1410,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { forIdentifier: .bloodPressureSystolic)! dataTypesDict[BODY_FAT_PERCENTAGE] = HKSampleType.quantityType( forIdentifier: .bodyFatPercentage)! + dataTypesDict[LEAN_BODY_MASS] = HKSampleType.quantityType(forIdentifier: .bodyMass)! dataTypesDict[BODY_MASS_INDEX] = HKSampleType.quantityType(forIdentifier: .bodyMassIndex)! dataTypesDict[BODY_TEMPERATURE] = HKSampleType.quantityType(forIdentifier: .bodyTemperature)! @@ -1507,6 +1509,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataQuantityTypesDict[BLOOD_PRESSURE_DIASTOLIC] = HKQuantityType.quantityType(forIdentifier: .bloodPressureDiastolic)! dataQuantityTypesDict[BLOOD_PRESSURE_SYSTOLIC] = HKQuantityType.quantityType(forIdentifier: .bloodPressureSystolic)! dataQuantityTypesDict[BODY_FAT_PERCENTAGE] = HKQuantityType.quantityType(forIdentifier: .bodyFatPercentage)! + dataQuantityTypesDict[LEAN_BODY_MASS] = HKSampleType.quantityType(forIdentifier: .bodyMass)! dataQuantityTypesDict[BODY_MASS_INDEX] = HKQuantityType.quantityType(forIdentifier: .bodyMassIndex)! dataQuantityTypesDict[BODY_TEMPERATURE] = HKQuantityType.quantityType(forIdentifier: .bodyTemperature)! diff --git a/packages/health/lib/src/heath_data_types.dart b/packages/health/lib/src/heath_data_types.dart index f50762721..0989381b9 100644 --- a/packages/health/lib/src/heath_data_types.dart +++ b/packages/health/lib/src/heath_data_types.dart @@ -11,6 +11,7 @@ enum HealthDataType { BLOOD_PRESSURE_DIASTOLIC, BLOOD_PRESSURE_SYSTOLIC, BODY_FAT_PERCENTAGE, + LEAN_BODY_MASS, BODY_MASS_INDEX, BODY_TEMPERATURE, BODY_WATER_MASS, @@ -125,6 +126,7 @@ const List dataTypeKeysIOS = [ HealthDataType.BLOOD_PRESSURE_DIASTOLIC, HealthDataType.BLOOD_PRESSURE_SYSTOLIC, HealthDataType.BODY_FAT_PERCENTAGE, + HealthDataType.LEAN_BODY_MASS, HealthDataType.BODY_MASS_INDEX, HealthDataType.BODY_TEMPERATURE, HealthDataType.DIETARY_CARBS_CONSUMED, @@ -216,6 +218,7 @@ const List dataTypeKeysAndroid = [ HealthDataType.BLOOD_PRESSURE_DIASTOLIC, HealthDataType.BLOOD_PRESSURE_SYSTOLIC, HealthDataType.BODY_FAT_PERCENTAGE, + HealthDataType.LEAN_BODY_MASS, HealthDataType.BODY_MASS_INDEX, HealthDataType.BODY_TEMPERATURE, HealthDataType.BODY_WATER_MASS, @@ -256,6 +259,7 @@ const Map dataTypeToUnit = { HealthDataType.BLOOD_PRESSURE_DIASTOLIC: HealthDataUnit.MILLIMETER_OF_MERCURY, HealthDataType.BLOOD_PRESSURE_SYSTOLIC: HealthDataUnit.MILLIMETER_OF_MERCURY, HealthDataType.BODY_FAT_PERCENTAGE: HealthDataUnit.PERCENT, + HealthDataType.LEAN_BODY_MASS: HealthDataUnit.KILOGRAM, HealthDataType.BODY_MASS_INDEX: HealthDataUnit.NO_UNIT, HealthDataType.BODY_TEMPERATURE: HealthDataUnit.DEGREE_CELSIUS, HealthDataType.BODY_WATER_MASS: HealthDataUnit.KILOGRAM, From 8950271e69c39486b43db7e0bf8c34960ad91206 Mon Sep 17 00:00:00 2001 From: Daniel Cachapa Date: Thu, 14 Nov 2024 17:45:46 +0100 Subject: [PATCH 53/80] Add missing conversion from LeanMassRecord --- .../cachet/plugins/health/HealthPlugin.kt | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 23f64dad7..846d5370e 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -1084,6 +1084,29 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ), ) + is LeanMassRecord -> + return listOf( + mapOf( + "uuid" to + metadata.id, + "value" to + record.weight + .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( @@ -2456,7 +2479,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, From aebf51527a636863a62b761375263ee20808c55d Mon Sep 17 00:00:00 2001 From: Daniel Cachapa Date: Fri, 15 Nov 2024 17:18:53 +0100 Subject: [PATCH 54/80] Fix wrong field name in Android --- .../src/main/kotlin/cachet/plugins/health/HealthPlugin.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 846d5370e..002e824b3 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -1084,13 +1084,13 @@ class HealthPlugin(private var channel: MethodChannel? = null) : ), ) - is LeanMassRecord -> + is LeanBodyMassRecord -> return listOf( mapOf( "uuid" to metadata.id, "value" to - record.weight + record.mass .inKilograms, "date_from" to record.time From 26c15b6409b7dbbdc19bd882c773111e121cc017 Mon Sep 17 00:00:00 2001 From: Daniel Cachapa Date: Fri, 15 Nov 2024 17:19:24 +0100 Subject: [PATCH 55/80] Fix wrong type name in iOS --- packages/health/ios/Classes/SwiftHealthPlugin.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index 79da70922..fcee46767 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -1410,7 +1410,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { forIdentifier: .bloodPressureSystolic)! dataTypesDict[BODY_FAT_PERCENTAGE] = HKSampleType.quantityType( forIdentifier: .bodyFatPercentage)! - dataTypesDict[LEAN_BODY_MASS] = HKSampleType.quantityType(forIdentifier: .bodyMass)! + dataTypesDict[LEAN_BODY_MASS] = HKSampleType.quantityType(forIdentifier: .leanBodyMass)! dataTypesDict[BODY_MASS_INDEX] = HKSampleType.quantityType(forIdentifier: .bodyMassIndex)! dataTypesDict[BODY_TEMPERATURE] = HKSampleType.quantityType(forIdentifier: .bodyTemperature)! @@ -1471,7 +1471,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { forIdentifier: .waistCircumference)! dataTypesDict[WALKING_HEART_RATE] = HKSampleType.quantityType( forIdentifier: .walkingHeartRateAverage)! - dataTypesDict[WEIGHT] = HKSampleType.quantityType(forIdentifier: .bodyMass)! + dataTypesDict[WEIGHT] = HKSampleType.quantityType(forIdentifier: .leanBodyMass)! dataTypesDict[DISTANCE_WALKING_RUNNING] = HKSampleType.quantityType( forIdentifier: .distanceWalkingRunning)! dataTypesDict[DISTANCE_SWIMMING] = HKSampleType.quantityType(forIdentifier: .distanceSwimming)! @@ -1509,7 +1509,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataQuantityTypesDict[BLOOD_PRESSURE_DIASTOLIC] = HKQuantityType.quantityType(forIdentifier: .bloodPressureDiastolic)! dataQuantityTypesDict[BLOOD_PRESSURE_SYSTOLIC] = HKQuantityType.quantityType(forIdentifier: .bloodPressureSystolic)! dataQuantityTypesDict[BODY_FAT_PERCENTAGE] = HKQuantityType.quantityType(forIdentifier: .bodyFatPercentage)! - dataQuantityTypesDict[LEAN_BODY_MASS] = HKSampleType.quantityType(forIdentifier: .bodyMass)! + dataQuantityTypesDict[LEAN_BODY_MASS] = HKSampleType.quantityType(forIdentifier: .leanBodyMass)! dataQuantityTypesDict[BODY_MASS_INDEX] = HKQuantityType.quantityType(forIdentifier: .bodyMassIndex)! dataQuantityTypesDict[BODY_TEMPERATURE] = HKQuantityType.quantityType(forIdentifier: .bodyTemperature)! From a0de7bdeb64e7b168643ffe1bd9e92e65c561ac5 Mon Sep 17 00:00:00 2001 From: Daniel Cachapa Date: Sat, 16 Nov 2024 20:22:13 +0100 Subject: [PATCH 56/80] Fix weight type --- packages/health/ios/Classes/SwiftHealthPlugin.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index fcee46767..d2fce50c0 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -1471,7 +1471,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { forIdentifier: .waistCircumference)! dataTypesDict[WALKING_HEART_RATE] = HKSampleType.quantityType( forIdentifier: .walkingHeartRateAverage)! - dataTypesDict[WEIGHT] = HKSampleType.quantityType(forIdentifier: .leanBodyMass)! + dataTypesDict[WEIGHT] = HKSampleType.quantityType(forIdentifier: .bodyMass)! dataTypesDict[DISTANCE_WALKING_RUNNING] = HKSampleType.quantityType( forIdentifier: .distanceWalkingRunning)! dataTypesDict[DISTANCE_SWIMMING] = HKSampleType.quantityType(forIdentifier: .distanceSwimming)! From 2578bc212c44415fcf756aaffa36f5a026929e3f Mon Sep 17 00:00:00 2001 From: Andrey Danilov Date: Tue, 24 Dec 2024 16:50:15 -0800 Subject: [PATCH 57/80] remove non-null check for name --- .../src/main/kotlin/cachet/plugins/health/HealthPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 216aff7ea..5c261f90f 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -1516,7 +1516,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] From 988cce86e3067fb7aa90196808113cd4aec0ea2e Mon Sep 17 00:00:00 2001 From: bernd70 Date: Sat, 28 Dec 2024 01:02:08 +0100 Subject: [PATCH 58/80] Deleting entries only selects own entries When deleting entries on iOS a compound predicate is used to limit the query to entries the calling app has created. Otherwise the call fails is the sample contains entries from other apps. --- .../ios/Classes/SwiftHealthPlugin.swift | 303 +++++++++--------- 1 file changed, 153 insertions(+), 150 deletions(-) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index d7af83e05..fb9e1fe49 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -10,7 +10,7 @@ enum RecordingMethod: Int { } public class SwiftHealthPlugin: NSObject, FlutterPlugin { - + let healthStore = HKHealthStore() var healthDataTypes = [HKSampleType]() var healthDataQuantityTypes = [HKQuantityType]() @@ -24,7 +24,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { var workoutActivityTypeMap: [String: HKWorkoutActivityType] = [:] var characteristicsTypesDict: [String: HKCharacteristicType] = [:] var nutritionList: [String] = [] - + // Health Data Type Keys let ACTIVE_ENERGY_BURNED = "ACTIVE_ENERGY_BURNED" let ATRIAL_FIBRILLATION_BURDEN = "ATRIAL_FIBRILLATION_BURDEN" @@ -147,7 +147,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let SLEEP_IN_BED = "SLEEP_IN_BED" let SLEEP_LIGHT = "SLEEP_LIGHT" let SLEEP_REM = "SLEEP_REM" - + let EXERCISE_TIME = "EXERCISE_TIME" let WORKOUT = "WORKOUT" let HEADACHE_UNSPECIFIED = "HEADACHE_UNSPECIFIED" @@ -161,8 +161,8 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let GENDER = "GENDER" let BLOOD_TYPE = "BLOOD_TYPE" let MENSTRUATION_FLOW = "MENSTRUATION_FLOW" - - + + // Health Unit types // MOLE_UNIT_WITH_MOLAR_MASS, // requires molar mass input - not supported yet // MOLE_UNIT_WITH_PREFIX_MOLAR_MASS, // requires molar mass & prefix input - not supported yet @@ -214,22 +214,22 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let MILLIGRAM_PER_DECILITER = "MILLIGRAM_PER_DECILITER" let UNKNOWN_UNIT = "UNKNOWN_UNIT" let NO_UNIT = "NO_UNIT" - + struct PluginError: Error { let message: String } - + public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel( name: "flutter_health", binaryMessenger: registrar.messenger()) let instance = SwiftHealthPlugin() registrar.addMethodCallDelegate(instance, channel: channel) } - + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { // Set up all data types initializeTypes() - + /// Handle checkIfHealthDataAvailable if call.method.elementsEqual("checkIfHealthDataAvailable") { checkIfHealthDataAvailable(call: call, result: result) @@ -237,72 +237,72 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else if call.method.elementsEqual("requestAuthorization") { try! requestAuthorization(call: call, result: result) } - + /// Handle getData else if call.method.elementsEqual("getData") { getData(call: call, result: result) } - + /// Handle getIntervalData else if (call.method.elementsEqual("getIntervalData")){ getIntervalData(call: call, result: result) } - + /// Handle getTotalStepsInInterval else if call.method.elementsEqual("getTotalStepsInInterval") { getTotalStepsInInterval(call: call, result: result) } - + /// Handle writeData else if call.method.elementsEqual("writeData") { try! writeData(call: call, result: result) } - + /// Handle writeAudiogram else if call.method.elementsEqual("writeAudiogram") { try! writeAudiogram(call: call, result: result) } - + /// Handle writeBloodPressure else if call.method.elementsEqual("writeBloodPressure") { try! writeBloodPressure(call: call, result: result) } - + /// Handle writeMeal else if (call.method.elementsEqual("writeMeal")){ try! writeMeal(call: call, result: result) } - + /// Handle writeInsulinDelivery else if (call.method.elementsEqual("writeInsulinDelivery")){ try! writeInsulinDelivery(call: call, result: result) } - + /// Handle writeWorkoutData else if call.method.elementsEqual("writeWorkoutData") { try! writeWorkoutData(call: call, result: result) } - + /// Handle writeMenstruationFlow else if call.method.elementsEqual("writeMenstruationFlow") { try! writeMenstruationFlow(call: call, result: result) } - + /// Handle hasPermission else if call.method.elementsEqual("hasPermissions") { try! hasPermissions(call: call, result: result) } - + /// Handle delete data else if call.method.elementsEqual("delete") { try! delete(call: call, result: result) } } - + func checkIfHealthDataAvailable(call: FlutterMethodCall, result: @escaping FlutterResult) { result(HKHealthStore.isHealthDataAvailable()) } - + func hasPermissions(call: FlutterMethodCall, result: @escaping FlutterResult) throws { let arguments = call.arguments as? NSDictionary guard var types = arguments?["types"] as? [String], @@ -311,18 +311,18 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments!") } - + if let nutritionIndex = types.firstIndex(of: NUTRITION) { types.remove(at: nutritionIndex) let nutritionPermission = permissions[nutritionIndex] permissions.remove(at: nutritionIndex) - + for nutritionType in nutritionList { types.append(nutritionType) permissions.append(nutritionPermission) } } - + for (index, type) in types.enumerated() { let sampleType = dataTypeLookUp(key: type) let success = hasPermission(type: sampleType, access: permissions[index]) @@ -338,12 +338,12 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } } } - + result(false) } - + func hasPermission(type: HKObjectType, access: Int) -> Bool? { - + if #available(iOS 13.0, *) { let status = healthStore.authorizationStatus(for: type) switch access { @@ -358,7 +358,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { return nil } } - + func requestAuthorization(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let types = arguments["types"] as? [String], @@ -367,7 +367,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments!") } - + var typesToRead = Set() var typesToWrite = Set() for (index, key) in types.enumerated() { @@ -401,7 +401,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } } } - + if #available(iOS 13.0, *) { healthStore.requestAuthorization(toShare: typesToWrite, read: typesToRead) { (success, error) in @@ -413,7 +413,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { result(false) // Handle the error here. } } - + func writeData(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let value = (arguments["value"] as? Double), @@ -425,7 +425,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments") } - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) @@ -433,9 +433,9 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let metadata: [String: Any] = [ HKMetadataKeyWasUserEntered: NSNumber(value: isManualEntry) ] - + let sample: HKObject - + if dataTypeLookUp(key: type).isKind(of: HKCategoryType.self) { sample = HKCategorySample( type: dataTypeLookUp(key: type) as! HKCategoryType, value: Int(value), start: dateFrom, @@ -446,7 +446,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { type: dataTypeLookUp(key: type) as! HKQuantityType, quantity: quantity, start: dateFrom, end: dateTo, metadata: metadata) } - + HKHealthStore().save( sample, withCompletion: { (success, error) in @@ -458,7 +458,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func writeAudiogram(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let frequencies = (arguments["frequencies"] as? [Double]), @@ -469,12 +469,12 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments") } - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + var sensitivityPoints = [HKAudiogramSensitivityPoint]() - + for index in 0...frequencies.count - 1 { let frequency = HKQuantity(unit: HKUnit.hertz(), doubleValue: frequencies[index]) let dbUnit = HKUnit.decibelHearingLevel() @@ -484,23 +484,23 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { frequency: frequency, leftEarSensitivity: left, rightEarSensitivity: right) sensitivityPoints.append(sensitivityPoint) } - + let audiogram: HKAudiogramSample let metadataReceived = (arguments["metadata"] as? [String: Any]?) - + if (metadataReceived) != nil { guard let deviceName = metadataReceived?!["HKDeviceName"] as? String else { return } guard let externalUUID = metadataReceived?!["HKExternalUUID"] as? String else { return } - + audiogram = HKAudiogramSample( sensitivityPoints: sensitivityPoints, start: dateFrom, end: dateTo, metadata: [HKMetadataKeyDeviceName: deviceName, HKMetadataKeyExternalUUID: externalUUID]) - + } else { audiogram = HKAudiogramSample( sensitivityPoints: sensitivityPoints, start: dateFrom, end: dateTo, metadata: nil) } - + HKHealthStore().save( audiogram, withCompletion: { (success, error) in @@ -512,7 +512,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func writeBloodPressure(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let systolic = (arguments["systolic"] as? Double), @@ -530,7 +530,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let metadata = [ HKMetadataKeyWasUserEntered: NSNumber(value: isManualEntry) ] - + let systolic_sample = HKQuantitySample( type: HKSampleType.quantityType(forIdentifier: .bloodPressureSystolic)!, quantity: HKQuantity(unit: HKUnit.millimeterOfMercury(), doubleValue: systolic), @@ -542,7 +542,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let bpCorrelationType = HKCorrelationType.correlationType(forIdentifier: .bloodPressure)! let bpCorrelation = Set(arrayLiteral: systolic_sample, diastolic_sample) let blood_pressure_sample = HKCorrelation(type: bpCorrelationType , start: dateFrom, end: dateTo, objects: bpCorrelation) - + HKHealthStore().save( [blood_pressure_sample], withCompletion: { (success, error) in @@ -554,7 +554,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func writeMeal(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let name = (arguments["name"] as? String?), @@ -565,14 +565,14 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments") } - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let mealTypeString = mealType ?? "UNKNOWN" let isManualEntry = recordingMethod == RecordingMethod.manual.rawValue - + var metadata = ["HKFoodMeal": mealTypeString, HKMetadataKeyWasUserEntered: NSNumber(value: isManualEntry)] as [String : Any] if (name != nil) { metadata[HKMetadataKeyFoodType] = "\(name!)" @@ -587,11 +587,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { type: HKSampleType.quantityType(forIdentifier: identifier)!, quantity: HKQuantity(unit: unit, doubleValue: unwrappedValue), start: dateFrom, end: dateTo, metadata: metadata) nutrition.insert(nutritionSample) } - + if #available(iOS 15.0, *){ let type = HKCorrelationType.correlationType(forIdentifier: HKCorrelationTypeIdentifier.food)! let meal = HKCorrelation(type: type, start: dateFrom, end: dateTo, objects: nutrition, metadata: metadata) - + HKHealthStore().save(meal, withCompletion: { (success, error) in if let err = error { print("Error Saving Meal Sample: \(err.localizedDescription)") @@ -615,13 +615,13 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let type = HKSampleType.quantityType(forIdentifier: .insulinDelivery)! let quantity = HKQuantity(unit: HKUnit.internationalUnit(), doubleValue: units) let metadata = [HKMetadataKeyInsulinDeliveryReason: reason] - + let insulin_sample = HKQuantitySample(type: type, quantity: quantity, start: dateFrom, end: dateTo, metadata: metadata) - + HKHealthStore().save(insulin_sample, withCompletion: { (success, error) in if let err = error { print("Error Saving Insulin Delivery Sample: \(err.localizedDescription)") @@ -631,7 +631,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func writeMenstruationFlow(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let flow = (arguments["value"] as? Int), @@ -644,7 +644,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { guard let menstrualFlowType = HKCategoryValueMenstrualFlow(rawValue: flow) else { throw PluginError(message: "Invalid Menstrual Flow Type") } - + let dateTime = Date(timeIntervalSince1970: endTime.doubleValue / 1000) let isManualEntry = recordingMethod == RecordingMethod.manual.rawValue @@ -654,15 +654,15 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } let metadata = [HKMetadataKeyMenstrualCycleStart: isStartOfCycle, HKMetadataKeyWasUserEntered: NSNumber(value: isManualEntry)] as [String : Any] - + let sample = HKCategorySample( type: categoryType, - value: menstrualFlowType.rawValue, - start: dateTime, + value: menstrualFlowType.rawValue, + start: dateTime, end: dateTime, metadata: metadata ) - + HKHealthStore().save( sample, withCompletion: { (success, error) in @@ -674,7 +674,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func writeWorkoutData(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let activityType = (arguments["activityType"] as? String), @@ -684,10 +684,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments - activityType, startTime or endTime invalid") } - + var totalEnergyBurned: HKQuantity? var totalDistance: HKQuantity? = nil - + // Handle optional arguments if let teb = (arguments["totalEnergyBurned"] as? Double) { totalEnergyBurned = HKQuantity( @@ -697,17 +697,17 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { totalDistance = HKQuantity( unit: unitDict[(arguments["totalDistanceUnit"] as! String)]!, doubleValue: td) } - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + var workout: HKWorkout - + workout = HKWorkout( activityType: ac, start: dateFrom, end: dateTo, duration: dateTo.timeIntervalSince(dateFrom), totalEnergyBurned: totalEnergyBurned ?? nil, totalDistance: totalDistance ?? nil, metadata: nil) - + HKHealthStore().save( workout, withCompletion: { (success, error) in @@ -719,33 +719,36 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func delete(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary let dataTypeKey = (arguments?["dataTypeKey"] as? String)! let startTime = (arguments?["startTime"] as? NSNumber) ?? 0 let endTime = (arguments?["endTime"] as? NSNumber) ?? 0 - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let dataType = dataTypeLookUp(key: dataTypeKey) - - let predicate = HKQuery.predicateForSamples( + + let samplePredicate = HKQuery.predicateForSamples( withStart: dateFrom, end: dateTo, options: .strictStartDate) + let ownerPredicate = HKQuery.predicateForObjects(from: HKSource.default()) let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false) - + let deleteQuery = HKSampleQuery( - sampleType: dataType, predicate: predicate, limit: HKObjectQueryNoLimit, + sampleType: dataType, + predicate: NSCompoundPredicate(andPredicateWithSubpredicates: [samplePredicate, ownerPredicate]), + limit: HKObjectQueryNoLimit, sortDescriptors: [sortDescriptor] ) { [self] x, samplesOrNil, error in - + guard let samplesOrNil = samplesOrNil, error == nil else { // Handle the error if necessary print("Error deleting \(dataType)") return } - + // Delete the retrieved objects from the HealthKit store HKHealthStore().delete(samplesOrNil) { (success, error) in if let err = error { @@ -756,10 +759,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } } } - + HKHealthStore().execute(deleteQuery) } - + func getData(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary let dataTypeKey = (arguments?["dataTypeKey"] as? String)! @@ -769,20 +772,20 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let limit = (arguments?["limit"] as? Int) ?? HKObjectQueryNoLimit let recordingMethodsToFilter = (arguments?["recordingMethodsToFilter"] as? [Int]) ?? [] let includeManualEntry = !recordingMethodsToFilter.contains(RecordingMethod.manual.rawValue) - + // Convert dates from milliseconds to Date() let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let dataType = dataTypeLookUp(key: dataTypeKey) var unit: HKUnit? if let dataUnitKey = dataUnitKey { unit = unitDict[dataUnitKey] } - + let sourceIdForCharacteristic = "com.apple.Health" let sourceNameForCharacteristic = "Health" - + switch(dataTypeKey) { case "BIRTH_DATE": let dateOfBirth = getBirthDate() @@ -826,7 +829,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { default: break } - + var predicate = HKQuery.predicateForSamples( withStart: dateFrom, end: dateTo, options: .strictStartDate) if (!includeManualEntry) { @@ -834,13 +837,13 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { predicate = NSCompoundPredicate(type: .and, subpredicates: [predicate, manualPredicate]) } let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false) - + let query = HKSampleQuery( sampleType: dataType, predicate: predicate, limit: limit, sortDescriptors: [sortDescriptor] ) { [self] x, samplesOrNil, error in - + switch samplesOrNil { case let (samples as [HKQuantitySample]) as Any: let dictionaries = samples.map { sample -> NSDictionary in @@ -860,9 +863,9 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { DispatchQueue.main.async { result(dictionaries) } - + case var (samplesCategory as [HKCategorySample]) as Any: - + if dataTypeKey == self.SLEEP_IN_BED { samplesCategory = samplesCategory.filter { $0.value == 0 } } @@ -898,13 +901,13 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } let categories = samplesCategory.map { sample -> NSDictionary in var metadata: [String: Any] = [:] - + if let sampleMetadata = sample.metadata { for (key, value) in sampleMetadata { metadata[key] = value } } - + return [ "uuid": "\(sample.uuid)", "value": sample.value, @@ -919,9 +922,9 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { DispatchQueue.main.async { result(categories) } - + case let (samplesWorkout as [HKWorkout]) as Any: - + let dictionaries = samplesWorkout.map { sample -> NSDictionary in return [ "uuid": "\(sample.uuid)", @@ -942,11 +945,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "total_energy_burned": sample.totalEnergyBurned != nil ? Int(sample.totalEnergyBurned!.doubleValue(for: HKUnit.kilocalorie())) : 0 ] } - + DispatchQueue.main.async { result(dictionaries) } - + case let (samplesAudiogram as [HKAudiogramSample]) as Any: let dictionaries = samplesAudiogram.map { sample -> NSDictionary in var frequencies = [Double]() @@ -973,7 +976,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { DispatchQueue.main.async { result(dictionaries) } - + case let (nutritionSample as [HKCorrelation]) as Any: var foods: [[String: Any?]] = [] for food in nutritionSample { @@ -1007,11 +1010,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { foods.append(sampleDict) } } - + DispatchQueue.main.async { result(foods) } - + default: if #available(iOS 14.0, *), let ecgSamples = samplesOrNil as? [HKElectrocardiogram] { let dictionaries = ecgSamples.map(fetchEcgMeasurements) @@ -1026,10 +1029,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } } } - + HKHealthStore().execute(query) } - + @available(iOS 14.0, *) private func fetchEcgMeasurements(_ sample: HKElectrocardiogram) -> NSDictionary { let semaphore = DispatchSemaphore(value: 0) @@ -1063,7 +1066,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "source_name": sample.sourceRevision.source.name, ] } - + func getIntervalData(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary let dataTypeKey = (arguments?["dataTypeKey"] as? String) ?? "DEFAULT" @@ -1073,24 +1076,24 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let intervalInSecond = (arguments?["interval"] as? Int) ?? 1 let recordingMethodsToFilter = (arguments?["recordingMethodsToFilter"] as? [Int]) ?? [] let includeManualEntry = !recordingMethodsToFilter.contains(RecordingMethod.manual.rawValue) - + // Set interval in seconds. var interval = DateComponents() interval.second = intervalInSecond - + // Convert dates from milliseconds to Date() let dateFrom = Date(timeIntervalSince1970: startDate.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endDate.doubleValue / 1000) - + let quantityType: HKQuantityType! = dataQuantityTypesDict[dataTypeKey] var predicate = HKQuery.predicateForSamples(withStart: dateFrom, end: dateTo, options: []) if (!includeManualEntry) { let manualPredicate = NSPredicate(format: "metadata.%K != YES", HKMetadataKeyWasUserEntered) predicate = NSCompoundPredicate(type: .and, subpredicates: [predicate, manualPredicate]) } - + let query = HKStatisticsCollectionQuery(quantityType: quantityType, quantitySamplePredicate: predicate, options: [.cumulativeSum, .separateBySource], anchorDate: dateFrom, intervalComponents: interval) - + query.initialResultsHandler = { [weak self] _, statisticCollectionOrNil, error in guard let self = self else { @@ -1101,7 +1104,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return } - + // Error detected. if let error = error { print("Query error: \(error.localizedDescription)") @@ -1110,7 +1113,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return } - + guard let collection = statisticCollectionOrNil as? HKStatisticsCollection else { print("Unexpected result from query") DispatchQueue.main.async { @@ -1118,7 +1121,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return } - + var dictionaries = [[String: Any]]() collection.enumerateStatistics(from: dateFrom, to: dateTo) { [weak self] statisticData, _ in @@ -1127,7 +1130,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { print("Self is nil during enumeration") return } - + do { if let quantity = statisticData.sumQuantity(), let dataUnitKey = dataUnitKey, @@ -1151,18 +1154,18 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } HKHealthStore().execute(query) } - + func getTotalStepsInInterval(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary let startTime = (arguments?["startTime"] as? NSNumber) ?? 0 let endTime = (arguments?["endTime"] as? NSNumber) ?? 0 let recordingMethodsToFilter = (arguments?["recordingMethodsToFilter"] as? [Int]) ?? [] let includeManualEntry = !recordingMethodsToFilter.contains(RecordingMethod.manual.rawValue) - + // Convert dates from milliseconds to Date() let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let sampleType = HKQuantityType.quantityType(forIdentifier: .stepCount)! var predicate = HKQuery.predicateForSamples( withStart: dateFrom, end: dateTo, options: .strictStartDate) @@ -1170,53 +1173,53 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let manualPredicate = NSPredicate(format: "metadata.%K != YES", HKMetadataKeyWasUserEntered) predicate = NSCompoundPredicate(type: .and, subpredicates: [predicate, manualPredicate]) } - + let query = HKStatisticsQuery( quantityType: sampleType, quantitySamplePredicate: predicate, options: .cumulativeSum ) { query, queryResult, error in - + guard let queryResult = queryResult else { let error = error! as NSError print("Error getting total steps in interval \(error.localizedDescription)") - + DispatchQueue.main.async { result(nil) } return } - + var steps = 0.0 - + if let quantity = queryResult.sumQuantity() { let unit = HKUnit.count() steps = quantity.doubleValue(for: unit) } - + let totalSteps = Int(steps) DispatchQueue.main.async { result(totalSteps) } } - + HKHealthStore().execute(query) } - + func unitLookUp(key: String) -> HKUnit { guard let unit = unitDict[key] else { return HKUnit.count() } return unit } - + func dataTypeLookUp(key: String) -> HKSampleType { guard let dataType_ = dataTypesDict[key] else { return HKSampleType.quantityType(forIdentifier: .bodyMass)! } return dataType_ } - + func getGender() -> HKBiologicalSex? { var bioSex:HKBiologicalSex? do { @@ -1227,7 +1230,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return bioSex } - + func getBirthDate() -> Date? { var dob:Date? do { @@ -1238,7 +1241,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return dob } - + func getBloodType() -> HKBloodType? { var bloodType:HKBloodType? do { @@ -1249,7 +1252,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return bloodType } - + func initializeTypes() { // Initialize units unitDict[GRAM] = HKUnit.gram() @@ -1298,7 +1301,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { unitDict[MILLIGRAM_PER_DECILITER] = HKUnit.init(from: "mg/dL") unitDict[UNKNOWN_UNIT] = HKUnit.init(from: "") unitDict[NO_UNIT] = HKUnit.init(from: "") - + // Initialize workout types workoutActivityTypeMap["ARCHERY"] = .archery workoutActivityTypeMap["BOWLING"] = .bowling @@ -1402,7 +1405,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataTypesDict[RESPIRATORY_RATE] = HKSampleType.quantityType(forIdentifier: .respiratoryRate)! dataTypesDict[PERIPHERAL_PERFUSION_INDEX] = HKSampleType.quantityType( forIdentifier: .peripheralPerfusionIndex)! - + dataTypesDict[BLOOD_PRESSURE_DIASTOLIC] = HKSampleType.quantityType( forIdentifier: .bloodPressureDiastolic)! dataTypesDict[BLOOD_PRESSURE_SYSTOLIC] = HKSampleType.quantityType( @@ -1411,7 +1414,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { forIdentifier: .bodyFatPercentage)! dataTypesDict[BODY_MASS_INDEX] = HKSampleType.quantityType(forIdentifier: .bodyMassIndex)! dataTypesDict[BODY_TEMPERATURE] = HKSampleType.quantityType(forIdentifier: .bodyTemperature)! - + // Nutrition dataTypesDict[DIETARY_CARBS_CONSUMED] = HKSampleType.quantityType(forIdentifier: .dietaryCarbohydrates)! dataTypesDict[DIETARY_CAFFEINE] = HKSampleType.quantityType(forIdentifier: .dietaryCaffeine)! @@ -1452,7 +1455,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataTypesDict[DIETARY_MANGANESE] = HKSampleType.quantityType(forIdentifier: .dietaryManganese)! dataTypesDict[DIETARY_MOLYBDENUM] = HKSampleType.quantityType(forIdentifier: .dietaryMolybdenum)! dataTypesDict[DIETARY_SELENIUM] = HKSampleType.quantityType(forIdentifier: .dietarySelenium)! - + dataTypesDict[ELECTRODERMAL_ACTIVITY] = HKSampleType.quantityType( forIdentifier: .electrodermalActivity)! dataTypesDict[FORCED_EXPIRATORY_VOLUME] = HKSampleType.quantityType( @@ -1483,21 +1486,21 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataTypesDict[SLEEP_REM] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! dataTypesDict[SLEEP_ASLEEP] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! dataTypesDict[MENSTRUATION_FLOW] = HKSampleType.categoryType(forIdentifier: .menstrualFlow)! - - + + dataTypesDict[EXERCISE_TIME] = HKSampleType.quantityType(forIdentifier: .appleExerciseTime)! dataTypesDict[WORKOUT] = HKSampleType.workoutType() dataTypesDict[NUTRITION] = HKSampleType.correlationType( forIdentifier: .food)! - + healthDataTypes = Array(dataTypesDict.values) - + characteristicsTypesDict[BIRTH_DATE] = HKObjectType.characteristicType(forIdentifier: .dateOfBirth)! characteristicsTypesDict[GENDER] = HKObjectType.characteristicType(forIdentifier: .biologicalSex)! characteristicsTypesDict[BLOOD_TYPE] = HKObjectType.characteristicType(forIdentifier: .bloodType)! characteristicsDataTypes = Array(characteristicsTypesDict.values) } - + // Set up iOS 11 specific types (ordinary health data quantity types) if #available(iOS 11.0, *) { dataQuantityTypesDict[ACTIVE_ENERGY_BURNED] = HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned)! @@ -1509,7 +1512,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataQuantityTypesDict[BODY_FAT_PERCENTAGE] = HKQuantityType.quantityType(forIdentifier: .bodyFatPercentage)! dataQuantityTypesDict[BODY_MASS_INDEX] = HKQuantityType.quantityType(forIdentifier: .bodyMassIndex)! dataQuantityTypesDict[BODY_TEMPERATURE] = HKQuantityType.quantityType(forIdentifier: .bodyTemperature)! - + // Nutrition dataQuantityTypesDict[DIETARY_CARBS_CONSUMED] = HKSampleType.quantityType(forIdentifier: .dietaryCarbohydrates)! dataQuantityTypesDict[DIETARY_CAFFEINE] = HKSampleType.quantityType(forIdentifier: .dietaryCaffeine)! @@ -1550,7 +1553,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataQuantityTypesDict[DIETARY_MANGANESE] = HKSampleType.quantityType(forIdentifier: .dietaryManganese)! dataQuantityTypesDict[DIETARY_MOLYBDENUM] = HKSampleType.quantityType(forIdentifier: .dietaryMolybdenum)! dataQuantityTypesDict[DIETARY_SELENIUM] = HKSampleType.quantityType(forIdentifier: .dietarySelenium)! - + dataQuantityTypesDict[ELECTRODERMAL_ACTIVITY] = HKQuantityType.quantityType(forIdentifier: .electrodermalActivity)! dataQuantityTypesDict[FORCED_EXPIRATORY_VOLUME] = HKQuantityType.quantityType(forIdentifier: .forcedExpiratoryVolume1)! dataQuantityTypesDict[HEART_RATE] = HKQuantityType.quantityType(forIdentifier: .heartRate)! @@ -1565,10 +1568,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataQuantityTypesDict[DISTANCE_SWIMMING] = HKQuantityType.quantityType(forIdentifier: .distanceSwimming)! dataQuantityTypesDict[DISTANCE_CYCLING] = HKQuantityType.quantityType(forIdentifier: .distanceCycling)! dataQuantityTypesDict[FLIGHTS_CLIMBED] = HKQuantityType.quantityType(forIdentifier: .flightsClimbed)! - + healthDataQuantityTypes = Array(dataQuantityTypesDict.values) } - + // Set up heart rate data types specific to the apple watch, requires iOS 12 if #available(iOS 12.2, *) { dataTypesDict[HIGH_HEART_RATE_EVENT] = HKSampleType.categoryType( @@ -1577,32 +1580,32 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { forIdentifier: .lowHeartRateEvent)! dataTypesDict[IRREGULAR_HEART_RATE_EVENT] = HKSampleType.categoryType( forIdentifier: .irregularHeartRhythmEvent)! - + heartRateEventTypes = Set([ HKSampleType.categoryType(forIdentifier: .highHeartRateEvent)!, HKSampleType.categoryType(forIdentifier: .lowHeartRateEvent)!, HKSampleType.categoryType(forIdentifier: .irregularHeartRhythmEvent)!, ]) } - + if #available(iOS 13.6, *) { dataTypesDict[HEADACHE_UNSPECIFIED] = HKSampleType.categoryType(forIdentifier: .headache)! dataTypesDict[HEADACHE_NOT_PRESENT] = HKSampleType.categoryType(forIdentifier: .headache)! dataTypesDict[HEADACHE_MILD] = HKSampleType.categoryType(forIdentifier: .headache)! dataTypesDict[HEADACHE_MODERATE] = HKSampleType.categoryType(forIdentifier: .headache)! dataTypesDict[HEADACHE_SEVERE] = HKSampleType.categoryType(forIdentifier: .headache)! - + headacheType = Set([ HKSampleType.categoryType(forIdentifier: .headache)! ]) } - + if #available(iOS 14.0, *) { dataTypesDict[ELECTROCARDIOGRAM] = HKSampleType.electrocardiogramType() - + unitDict[VOLT] = HKUnit.volt() unitDict[INCHES_OF_MERCURY] = HKUnit.inchesOfMercury() - + workoutActivityTypeMap["CARDIO_DANCE"] = HKWorkoutActivityType.cardioDance workoutActivityTypeMap["SOCIAL_DANCE"] = HKWorkoutActivityType.socialDance workoutActivityTypeMap["PICKLEBALL"] = HKWorkoutActivityType.pickleball @@ -1611,13 +1614,13 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { if #available(iOS 16.0, *) { dataTypesDict[ATRIAL_FIBRILLATION_BURDEN] = HKQuantityType.quantityType(forIdentifier: .atrialFibrillationBurden)! - } - + } + // Concatenate heart events, headache and health data types (both may be empty) allDataTypes = Set(heartRateEventTypes + healthDataTypes) allDataTypes = allDataTypes.union(headacheType) } - + func getWorkoutType(type: HKWorkoutActivityType) -> String { switch type { case .americanFootball: From c1ddc2c035e4476cca4f96fcacaa2228f4259eb8 Mon Sep 17 00:00:00 2001 From: bernd70 Date: Sat, 28 Dec 2024 01:27:30 +0100 Subject: [PATCH 59/80] Undid whitespace changes --- .../ios/Classes/SwiftHealthPlugin.swift | 296 +++++++++--------- 1 file changed, 148 insertions(+), 148 deletions(-) diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index fb9e1fe49..7bb71ca2b 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -10,7 +10,7 @@ enum RecordingMethod: Int { } public class SwiftHealthPlugin: NSObject, FlutterPlugin { - + let healthStore = HKHealthStore() var healthDataTypes = [HKSampleType]() var healthDataQuantityTypes = [HKQuantityType]() @@ -24,7 +24,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { var workoutActivityTypeMap: [String: HKWorkoutActivityType] = [:] var characteristicsTypesDict: [String: HKCharacteristicType] = [:] var nutritionList: [String] = [] - + // Health Data Type Keys let ACTIVE_ENERGY_BURNED = "ACTIVE_ENERGY_BURNED" let ATRIAL_FIBRILLATION_BURDEN = "ATRIAL_FIBRILLATION_BURDEN" @@ -147,7 +147,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let SLEEP_IN_BED = "SLEEP_IN_BED" let SLEEP_LIGHT = "SLEEP_LIGHT" let SLEEP_REM = "SLEEP_REM" - + let EXERCISE_TIME = "EXERCISE_TIME" let WORKOUT = "WORKOUT" let HEADACHE_UNSPECIFIED = "HEADACHE_UNSPECIFIED" @@ -161,8 +161,8 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let GENDER = "GENDER" let BLOOD_TYPE = "BLOOD_TYPE" let MENSTRUATION_FLOW = "MENSTRUATION_FLOW" - - + + // Health Unit types // MOLE_UNIT_WITH_MOLAR_MASS, // requires molar mass input - not supported yet // MOLE_UNIT_WITH_PREFIX_MOLAR_MASS, // requires molar mass & prefix input - not supported yet @@ -214,22 +214,22 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let MILLIGRAM_PER_DECILITER = "MILLIGRAM_PER_DECILITER" let UNKNOWN_UNIT = "UNKNOWN_UNIT" let NO_UNIT = "NO_UNIT" - + struct PluginError: Error { let message: String } - + public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel( name: "flutter_health", binaryMessenger: registrar.messenger()) let instance = SwiftHealthPlugin() registrar.addMethodCallDelegate(instance, channel: channel) } - + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { // Set up all data types initializeTypes() - + /// Handle checkIfHealthDataAvailable if call.method.elementsEqual("checkIfHealthDataAvailable") { checkIfHealthDataAvailable(call: call, result: result) @@ -237,72 +237,72 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else if call.method.elementsEqual("requestAuthorization") { try! requestAuthorization(call: call, result: result) } - + /// Handle getData else if call.method.elementsEqual("getData") { getData(call: call, result: result) } - + /// Handle getIntervalData else if (call.method.elementsEqual("getIntervalData")){ getIntervalData(call: call, result: result) } - + /// Handle getTotalStepsInInterval else if call.method.elementsEqual("getTotalStepsInInterval") { getTotalStepsInInterval(call: call, result: result) } - + /// Handle writeData else if call.method.elementsEqual("writeData") { try! writeData(call: call, result: result) } - + /// Handle writeAudiogram else if call.method.elementsEqual("writeAudiogram") { try! writeAudiogram(call: call, result: result) } - + /// Handle writeBloodPressure else if call.method.elementsEqual("writeBloodPressure") { try! writeBloodPressure(call: call, result: result) } - + /// Handle writeMeal else if (call.method.elementsEqual("writeMeal")){ try! writeMeal(call: call, result: result) } - + /// Handle writeInsulinDelivery else if (call.method.elementsEqual("writeInsulinDelivery")){ try! writeInsulinDelivery(call: call, result: result) } - + /// Handle writeWorkoutData else if call.method.elementsEqual("writeWorkoutData") { try! writeWorkoutData(call: call, result: result) } - + /// Handle writeMenstruationFlow else if call.method.elementsEqual("writeMenstruationFlow") { try! writeMenstruationFlow(call: call, result: result) } - + /// Handle hasPermission else if call.method.elementsEqual("hasPermissions") { try! hasPermissions(call: call, result: result) } - + /// Handle delete data else if call.method.elementsEqual("delete") { try! delete(call: call, result: result) } } - + func checkIfHealthDataAvailable(call: FlutterMethodCall, result: @escaping FlutterResult) { result(HKHealthStore.isHealthDataAvailable()) } - + func hasPermissions(call: FlutterMethodCall, result: @escaping FlutterResult) throws { let arguments = call.arguments as? NSDictionary guard var types = arguments?["types"] as? [String], @@ -311,18 +311,18 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments!") } - + if let nutritionIndex = types.firstIndex(of: NUTRITION) { types.remove(at: nutritionIndex) let nutritionPermission = permissions[nutritionIndex] permissions.remove(at: nutritionIndex) - + for nutritionType in nutritionList { types.append(nutritionType) permissions.append(nutritionPermission) } } - + for (index, type) in types.enumerated() { let sampleType = dataTypeLookUp(key: type) let success = hasPermission(type: sampleType, access: permissions[index]) @@ -338,12 +338,12 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } } } - + result(false) } - + func hasPermission(type: HKObjectType, access: Int) -> Bool? { - + if #available(iOS 13.0, *) { let status = healthStore.authorizationStatus(for: type) switch access { @@ -358,7 +358,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { return nil } } - + func requestAuthorization(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let types = arguments["types"] as? [String], @@ -367,7 +367,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments!") } - + var typesToRead = Set() var typesToWrite = Set() for (index, key) in types.enumerated() { @@ -401,7 +401,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } } } - + if #available(iOS 13.0, *) { healthStore.requestAuthorization(toShare: typesToWrite, read: typesToRead) { (success, error) in @@ -413,7 +413,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { result(false) // Handle the error here. } } - + func writeData(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let value = (arguments["value"] as? Double), @@ -425,7 +425,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments") } - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) @@ -433,9 +433,9 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let metadata: [String: Any] = [ HKMetadataKeyWasUserEntered: NSNumber(value: isManualEntry) ] - + let sample: HKObject - + if dataTypeLookUp(key: type).isKind(of: HKCategoryType.self) { sample = HKCategorySample( type: dataTypeLookUp(key: type) as! HKCategoryType, value: Int(value), start: dateFrom, @@ -446,7 +446,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { type: dataTypeLookUp(key: type) as! HKQuantityType, quantity: quantity, start: dateFrom, end: dateTo, metadata: metadata) } - + HKHealthStore().save( sample, withCompletion: { (success, error) in @@ -458,7 +458,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func writeAudiogram(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let frequencies = (arguments["frequencies"] as? [Double]), @@ -469,12 +469,12 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments") } - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + var sensitivityPoints = [HKAudiogramSensitivityPoint]() - + for index in 0...frequencies.count - 1 { let frequency = HKQuantity(unit: HKUnit.hertz(), doubleValue: frequencies[index]) let dbUnit = HKUnit.decibelHearingLevel() @@ -484,23 +484,23 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { frequency: frequency, leftEarSensitivity: left, rightEarSensitivity: right) sensitivityPoints.append(sensitivityPoint) } - + let audiogram: HKAudiogramSample let metadataReceived = (arguments["metadata"] as? [String: Any]?) - + if (metadataReceived) != nil { guard let deviceName = metadataReceived?!["HKDeviceName"] as? String else { return } guard let externalUUID = metadataReceived?!["HKExternalUUID"] as? String else { return } - + audiogram = HKAudiogramSample( sensitivityPoints: sensitivityPoints, start: dateFrom, end: dateTo, metadata: [HKMetadataKeyDeviceName: deviceName, HKMetadataKeyExternalUUID: externalUUID]) - + } else { audiogram = HKAudiogramSample( sensitivityPoints: sensitivityPoints, start: dateFrom, end: dateTo, metadata: nil) } - + HKHealthStore().save( audiogram, withCompletion: { (success, error) in @@ -512,7 +512,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func writeBloodPressure(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let systolic = (arguments["systolic"] as? Double), @@ -530,7 +530,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let metadata = [ HKMetadataKeyWasUserEntered: NSNumber(value: isManualEntry) ] - + let systolic_sample = HKQuantitySample( type: HKSampleType.quantityType(forIdentifier: .bloodPressureSystolic)!, quantity: HKQuantity(unit: HKUnit.millimeterOfMercury(), doubleValue: systolic), @@ -542,7 +542,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let bpCorrelationType = HKCorrelationType.correlationType(forIdentifier: .bloodPressure)! let bpCorrelation = Set(arrayLiteral: systolic_sample, diastolic_sample) let blood_pressure_sample = HKCorrelation(type: bpCorrelationType , start: dateFrom, end: dateTo, objects: bpCorrelation) - + HKHealthStore().save( [blood_pressure_sample], withCompletion: { (success, error) in @@ -554,7 +554,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func writeMeal(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let name = (arguments["name"] as? String?), @@ -565,14 +565,14 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments") } - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let mealTypeString = mealType ?? "UNKNOWN" let isManualEntry = recordingMethod == RecordingMethod.manual.rawValue - + var metadata = ["HKFoodMeal": mealTypeString, HKMetadataKeyWasUserEntered: NSNumber(value: isManualEntry)] as [String : Any] if (name != nil) { metadata[HKMetadataKeyFoodType] = "\(name!)" @@ -587,11 +587,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { type: HKSampleType.quantityType(forIdentifier: identifier)!, quantity: HKQuantity(unit: unit, doubleValue: unwrappedValue), start: dateFrom, end: dateTo, metadata: metadata) nutrition.insert(nutritionSample) } - + if #available(iOS 15.0, *){ let type = HKCorrelationType.correlationType(forIdentifier: HKCorrelationTypeIdentifier.food)! let meal = HKCorrelation(type: type, start: dateFrom, end: dateTo, objects: nutrition, metadata: metadata) - + HKHealthStore().save(meal, withCompletion: { (success, error) in if let err = error { print("Error Saving Meal Sample: \(err.localizedDescription)") @@ -615,13 +615,13 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let type = HKSampleType.quantityType(forIdentifier: .insulinDelivery)! let quantity = HKQuantity(unit: HKUnit.internationalUnit(), doubleValue: units) let metadata = [HKMetadataKeyInsulinDeliveryReason: reason] - + let insulin_sample = HKQuantitySample(type: type, quantity: quantity, start: dateFrom, end: dateTo, metadata: metadata) - + HKHealthStore().save(insulin_sample, withCompletion: { (success, error) in if let err = error { print("Error Saving Insulin Delivery Sample: \(err.localizedDescription)") @@ -631,7 +631,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func writeMenstruationFlow(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let flow = (arguments["value"] as? Int), @@ -644,7 +644,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { guard let menstrualFlowType = HKCategoryValueMenstrualFlow(rawValue: flow) else { throw PluginError(message: "Invalid Menstrual Flow Type") } - + let dateTime = Date(timeIntervalSince1970: endTime.doubleValue / 1000) let isManualEntry = recordingMethod == RecordingMethod.manual.rawValue @@ -654,15 +654,15 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } let metadata = [HKMetadataKeyMenstrualCycleStart: isStartOfCycle, HKMetadataKeyWasUserEntered: NSNumber(value: isManualEntry)] as [String : Any] - + let sample = HKCategorySample( type: categoryType, - value: menstrualFlowType.rawValue, - start: dateTime, + value: menstrualFlowType.rawValue, + start: dateTime, end: dateTime, metadata: metadata ) - + HKHealthStore().save( sample, withCompletion: { (success, error) in @@ -674,7 +674,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func writeWorkoutData(call: FlutterMethodCall, result: @escaping FlutterResult) throws { guard let arguments = call.arguments as? NSDictionary, let activityType = (arguments["activityType"] as? String), @@ -684,10 +684,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { else { throw PluginError(message: "Invalid Arguments - activityType, startTime or endTime invalid") } - + var totalEnergyBurned: HKQuantity? var totalDistance: HKQuantity? = nil - + // Handle optional arguments if let teb = (arguments["totalEnergyBurned"] as? Double) { totalEnergyBurned = HKQuantity( @@ -697,17 +697,17 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { totalDistance = HKQuantity( unit: unitDict[(arguments["totalDistanceUnit"] as! String)]!, doubleValue: td) } - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + var workout: HKWorkout - + workout = HKWorkout( activityType: ac, start: dateFrom, end: dateTo, duration: dateTo.timeIntervalSince(dateFrom), totalEnergyBurned: totalEnergyBurned ?? nil, totalDistance: totalDistance ?? nil, metadata: nil) - + HKHealthStore().save( workout, withCompletion: { (success, error) in @@ -719,36 +719,36 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } }) } - + func delete(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary let dataTypeKey = (arguments?["dataTypeKey"] as? String)! let startTime = (arguments?["startTime"] as? NSNumber) ?? 0 let endTime = (arguments?["endTime"] as? NSNumber) ?? 0 - + let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let dataType = dataTypeLookUp(key: dataTypeKey) - + let samplePredicate = HKQuery.predicateForSamples( withStart: dateFrom, end: dateTo, options: .strictStartDate) let ownerPredicate = HKQuery.predicateForObjects(from: HKSource.default()) let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false) - + let deleteQuery = HKSampleQuery( sampleType: dataType, predicate: NSCompoundPredicate(andPredicateWithSubpredicates: [samplePredicate, ownerPredicate]), limit: HKObjectQueryNoLimit, sortDescriptors: [sortDescriptor] ) { [self] x, samplesOrNil, error in - + guard let samplesOrNil = samplesOrNil, error == nil else { // Handle the error if necessary print("Error deleting \(dataType)") return } - + // Delete the retrieved objects from the HealthKit store HKHealthStore().delete(samplesOrNil) { (success, error) in if let err = error { @@ -759,10 +759,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } } } - + HKHealthStore().execute(deleteQuery) } - + func getData(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary let dataTypeKey = (arguments?["dataTypeKey"] as? String)! @@ -772,20 +772,20 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let limit = (arguments?["limit"] as? Int) ?? HKObjectQueryNoLimit let recordingMethodsToFilter = (arguments?["recordingMethodsToFilter"] as? [Int]) ?? [] let includeManualEntry = !recordingMethodsToFilter.contains(RecordingMethod.manual.rawValue) - + // Convert dates from milliseconds to Date() let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let dataType = dataTypeLookUp(key: dataTypeKey) var unit: HKUnit? if let dataUnitKey = dataUnitKey { unit = unitDict[dataUnitKey] } - + let sourceIdForCharacteristic = "com.apple.Health" let sourceNameForCharacteristic = "Health" - + switch(dataTypeKey) { case "BIRTH_DATE": let dateOfBirth = getBirthDate() @@ -829,7 +829,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { default: break } - + var predicate = HKQuery.predicateForSamples( withStart: dateFrom, end: dateTo, options: .strictStartDate) if (!includeManualEntry) { @@ -837,13 +837,13 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { predicate = NSCompoundPredicate(type: .and, subpredicates: [predicate, manualPredicate]) } let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false) - + let query = HKSampleQuery( sampleType: dataType, predicate: predicate, limit: limit, sortDescriptors: [sortDescriptor] ) { [self] x, samplesOrNil, error in - + switch samplesOrNil { case let (samples as [HKQuantitySample]) as Any: let dictionaries = samples.map { sample -> NSDictionary in @@ -863,9 +863,9 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { DispatchQueue.main.async { result(dictionaries) } - + case var (samplesCategory as [HKCategorySample]) as Any: - + if dataTypeKey == self.SLEEP_IN_BED { samplesCategory = samplesCategory.filter { $0.value == 0 } } @@ -901,13 +901,13 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } let categories = samplesCategory.map { sample -> NSDictionary in var metadata: [String: Any] = [:] - + if let sampleMetadata = sample.metadata { for (key, value) in sampleMetadata { metadata[key] = value } } - + return [ "uuid": "\(sample.uuid)", "value": sample.value, @@ -922,9 +922,9 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { DispatchQueue.main.async { result(categories) } - + case let (samplesWorkout as [HKWorkout]) as Any: - + let dictionaries = samplesWorkout.map { sample -> NSDictionary in return [ "uuid": "\(sample.uuid)", @@ -945,11 +945,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "total_energy_burned": sample.totalEnergyBurned != nil ? Int(sample.totalEnergyBurned!.doubleValue(for: HKUnit.kilocalorie())) : 0 ] } - + DispatchQueue.main.async { result(dictionaries) } - + case let (samplesAudiogram as [HKAudiogramSample]) as Any: let dictionaries = samplesAudiogram.map { sample -> NSDictionary in var frequencies = [Double]() @@ -976,7 +976,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { DispatchQueue.main.async { result(dictionaries) } - + case let (nutritionSample as [HKCorrelation]) as Any: var foods: [[String: Any?]] = [] for food in nutritionSample { @@ -1010,11 +1010,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { foods.append(sampleDict) } } - + DispatchQueue.main.async { result(foods) } - + default: if #available(iOS 14.0, *), let ecgSamples = samplesOrNil as? [HKElectrocardiogram] { let dictionaries = ecgSamples.map(fetchEcgMeasurements) @@ -1029,10 +1029,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } } } - + HKHealthStore().execute(query) } - + @available(iOS 14.0, *) private func fetchEcgMeasurements(_ sample: HKElectrocardiogram) -> NSDictionary { let semaphore = DispatchSemaphore(value: 0) @@ -1066,7 +1066,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "source_name": sample.sourceRevision.source.name, ] } - + func getIntervalData(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary let dataTypeKey = (arguments?["dataTypeKey"] as? String) ?? "DEFAULT" @@ -1076,24 +1076,24 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let intervalInSecond = (arguments?["interval"] as? Int) ?? 1 let recordingMethodsToFilter = (arguments?["recordingMethodsToFilter"] as? [Int]) ?? [] let includeManualEntry = !recordingMethodsToFilter.contains(RecordingMethod.manual.rawValue) - + // Set interval in seconds. var interval = DateComponents() interval.second = intervalInSecond - + // Convert dates from milliseconds to Date() let dateFrom = Date(timeIntervalSince1970: startDate.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endDate.doubleValue / 1000) - + let quantityType: HKQuantityType! = dataQuantityTypesDict[dataTypeKey] var predicate = HKQuery.predicateForSamples(withStart: dateFrom, end: dateTo, options: []) if (!includeManualEntry) { let manualPredicate = NSPredicate(format: "metadata.%K != YES", HKMetadataKeyWasUserEntered) predicate = NSCompoundPredicate(type: .and, subpredicates: [predicate, manualPredicate]) } - + let query = HKStatisticsCollectionQuery(quantityType: quantityType, quantitySamplePredicate: predicate, options: [.cumulativeSum, .separateBySource], anchorDate: dateFrom, intervalComponents: interval) - + query.initialResultsHandler = { [weak self] _, statisticCollectionOrNil, error in guard let self = self else { @@ -1104,7 +1104,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return } - + // Error detected. if let error = error { print("Query error: \(error.localizedDescription)") @@ -1113,7 +1113,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return } - + guard let collection = statisticCollectionOrNil as? HKStatisticsCollection else { print("Unexpected result from query") DispatchQueue.main.async { @@ -1121,7 +1121,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return } - + var dictionaries = [[String: Any]]() collection.enumerateStatistics(from: dateFrom, to: dateTo) { [weak self] statisticData, _ in @@ -1130,7 +1130,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { print("Self is nil during enumeration") return } - + do { if let quantity = statisticData.sumQuantity(), let dataUnitKey = dataUnitKey, @@ -1154,18 +1154,18 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } HKHealthStore().execute(query) } - + func getTotalStepsInInterval(call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary let startTime = (arguments?["startTime"] as? NSNumber) ?? 0 let endTime = (arguments?["endTime"] as? NSNumber) ?? 0 let recordingMethodsToFilter = (arguments?["recordingMethodsToFilter"] as? [Int]) ?? [] let includeManualEntry = !recordingMethodsToFilter.contains(RecordingMethod.manual.rawValue) - + // Convert dates from milliseconds to Date() let dateFrom = Date(timeIntervalSince1970: startTime.doubleValue / 1000) let dateTo = Date(timeIntervalSince1970: endTime.doubleValue / 1000) - + let sampleType = HKQuantityType.quantityType(forIdentifier: .stepCount)! var predicate = HKQuery.predicateForSamples( withStart: dateFrom, end: dateTo, options: .strictStartDate) @@ -1173,53 +1173,53 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let manualPredicate = NSPredicate(format: "metadata.%K != YES", HKMetadataKeyWasUserEntered) predicate = NSCompoundPredicate(type: .and, subpredicates: [predicate, manualPredicate]) } - + let query = HKStatisticsQuery( quantityType: sampleType, quantitySamplePredicate: predicate, options: .cumulativeSum ) { query, queryResult, error in - + guard let queryResult = queryResult else { let error = error! as NSError print("Error getting total steps in interval \(error.localizedDescription)") - + DispatchQueue.main.async { result(nil) } return } - + var steps = 0.0 - + if let quantity = queryResult.sumQuantity() { let unit = HKUnit.count() steps = quantity.doubleValue(for: unit) } - + let totalSteps = Int(steps) DispatchQueue.main.async { result(totalSteps) } } - + HKHealthStore().execute(query) } - + func unitLookUp(key: String) -> HKUnit { guard let unit = unitDict[key] else { return HKUnit.count() } return unit } - + func dataTypeLookUp(key: String) -> HKSampleType { guard let dataType_ = dataTypesDict[key] else { return HKSampleType.quantityType(forIdentifier: .bodyMass)! } return dataType_ } - + func getGender() -> HKBiologicalSex? { var bioSex:HKBiologicalSex? do { @@ -1230,7 +1230,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return bioSex } - + func getBirthDate() -> Date? { var dob:Date? do { @@ -1241,7 +1241,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return dob } - + func getBloodType() -> HKBloodType? { var bloodType:HKBloodType? do { @@ -1252,7 +1252,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { } return bloodType } - + func initializeTypes() { // Initialize units unitDict[GRAM] = HKUnit.gram() @@ -1301,7 +1301,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { unitDict[MILLIGRAM_PER_DECILITER] = HKUnit.init(from: "mg/dL") unitDict[UNKNOWN_UNIT] = HKUnit.init(from: "") unitDict[NO_UNIT] = HKUnit.init(from: "") - + // Initialize workout types workoutActivityTypeMap["ARCHERY"] = .archery workoutActivityTypeMap["BOWLING"] = .bowling @@ -1405,7 +1405,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataTypesDict[RESPIRATORY_RATE] = HKSampleType.quantityType(forIdentifier: .respiratoryRate)! dataTypesDict[PERIPHERAL_PERFUSION_INDEX] = HKSampleType.quantityType( forIdentifier: .peripheralPerfusionIndex)! - + dataTypesDict[BLOOD_PRESSURE_DIASTOLIC] = HKSampleType.quantityType( forIdentifier: .bloodPressureDiastolic)! dataTypesDict[BLOOD_PRESSURE_SYSTOLIC] = HKSampleType.quantityType( @@ -1414,7 +1414,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { forIdentifier: .bodyFatPercentage)! dataTypesDict[BODY_MASS_INDEX] = HKSampleType.quantityType(forIdentifier: .bodyMassIndex)! dataTypesDict[BODY_TEMPERATURE] = HKSampleType.quantityType(forIdentifier: .bodyTemperature)! - + // Nutrition dataTypesDict[DIETARY_CARBS_CONSUMED] = HKSampleType.quantityType(forIdentifier: .dietaryCarbohydrates)! dataTypesDict[DIETARY_CAFFEINE] = HKSampleType.quantityType(forIdentifier: .dietaryCaffeine)! @@ -1455,7 +1455,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataTypesDict[DIETARY_MANGANESE] = HKSampleType.quantityType(forIdentifier: .dietaryManganese)! dataTypesDict[DIETARY_MOLYBDENUM] = HKSampleType.quantityType(forIdentifier: .dietaryMolybdenum)! dataTypesDict[DIETARY_SELENIUM] = HKSampleType.quantityType(forIdentifier: .dietarySelenium)! - + dataTypesDict[ELECTRODERMAL_ACTIVITY] = HKSampleType.quantityType( forIdentifier: .electrodermalActivity)! dataTypesDict[FORCED_EXPIRATORY_VOLUME] = HKSampleType.quantityType( @@ -1486,21 +1486,21 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataTypesDict[SLEEP_REM] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! dataTypesDict[SLEEP_ASLEEP] = HKSampleType.categoryType(forIdentifier: .sleepAnalysis)! dataTypesDict[MENSTRUATION_FLOW] = HKSampleType.categoryType(forIdentifier: .menstrualFlow)! - - + + dataTypesDict[EXERCISE_TIME] = HKSampleType.quantityType(forIdentifier: .appleExerciseTime)! dataTypesDict[WORKOUT] = HKSampleType.workoutType() dataTypesDict[NUTRITION] = HKSampleType.correlationType( forIdentifier: .food)! - + healthDataTypes = Array(dataTypesDict.values) - + characteristicsTypesDict[BIRTH_DATE] = HKObjectType.characteristicType(forIdentifier: .dateOfBirth)! characteristicsTypesDict[GENDER] = HKObjectType.characteristicType(forIdentifier: .biologicalSex)! characteristicsTypesDict[BLOOD_TYPE] = HKObjectType.characteristicType(forIdentifier: .bloodType)! characteristicsDataTypes = Array(characteristicsTypesDict.values) } - + // Set up iOS 11 specific types (ordinary health data quantity types) if #available(iOS 11.0, *) { dataQuantityTypesDict[ACTIVE_ENERGY_BURNED] = HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned)! @@ -1512,7 +1512,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataQuantityTypesDict[BODY_FAT_PERCENTAGE] = HKQuantityType.quantityType(forIdentifier: .bodyFatPercentage)! dataQuantityTypesDict[BODY_MASS_INDEX] = HKQuantityType.quantityType(forIdentifier: .bodyMassIndex)! dataQuantityTypesDict[BODY_TEMPERATURE] = HKQuantityType.quantityType(forIdentifier: .bodyTemperature)! - + // Nutrition dataQuantityTypesDict[DIETARY_CARBS_CONSUMED] = HKSampleType.quantityType(forIdentifier: .dietaryCarbohydrates)! dataQuantityTypesDict[DIETARY_CAFFEINE] = HKSampleType.quantityType(forIdentifier: .dietaryCaffeine)! @@ -1553,7 +1553,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataQuantityTypesDict[DIETARY_MANGANESE] = HKSampleType.quantityType(forIdentifier: .dietaryManganese)! dataQuantityTypesDict[DIETARY_MOLYBDENUM] = HKSampleType.quantityType(forIdentifier: .dietaryMolybdenum)! dataQuantityTypesDict[DIETARY_SELENIUM] = HKSampleType.quantityType(forIdentifier: .dietarySelenium)! - + dataQuantityTypesDict[ELECTRODERMAL_ACTIVITY] = HKQuantityType.quantityType(forIdentifier: .electrodermalActivity)! dataQuantityTypesDict[FORCED_EXPIRATORY_VOLUME] = HKQuantityType.quantityType(forIdentifier: .forcedExpiratoryVolume1)! dataQuantityTypesDict[HEART_RATE] = HKQuantityType.quantityType(forIdentifier: .heartRate)! @@ -1568,10 +1568,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { dataQuantityTypesDict[DISTANCE_SWIMMING] = HKQuantityType.quantityType(forIdentifier: .distanceSwimming)! dataQuantityTypesDict[DISTANCE_CYCLING] = HKQuantityType.quantityType(forIdentifier: .distanceCycling)! dataQuantityTypesDict[FLIGHTS_CLIMBED] = HKQuantityType.quantityType(forIdentifier: .flightsClimbed)! - + healthDataQuantityTypes = Array(dataQuantityTypesDict.values) } - + // Set up heart rate data types specific to the apple watch, requires iOS 12 if #available(iOS 12.2, *) { dataTypesDict[HIGH_HEART_RATE_EVENT] = HKSampleType.categoryType( @@ -1580,32 +1580,32 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { forIdentifier: .lowHeartRateEvent)! dataTypesDict[IRREGULAR_HEART_RATE_EVENT] = HKSampleType.categoryType( forIdentifier: .irregularHeartRhythmEvent)! - + heartRateEventTypes = Set([ HKSampleType.categoryType(forIdentifier: .highHeartRateEvent)!, HKSampleType.categoryType(forIdentifier: .lowHeartRateEvent)!, HKSampleType.categoryType(forIdentifier: .irregularHeartRhythmEvent)!, ]) } - + if #available(iOS 13.6, *) { dataTypesDict[HEADACHE_UNSPECIFIED] = HKSampleType.categoryType(forIdentifier: .headache)! dataTypesDict[HEADACHE_NOT_PRESENT] = HKSampleType.categoryType(forIdentifier: .headache)! dataTypesDict[HEADACHE_MILD] = HKSampleType.categoryType(forIdentifier: .headache)! dataTypesDict[HEADACHE_MODERATE] = HKSampleType.categoryType(forIdentifier: .headache)! dataTypesDict[HEADACHE_SEVERE] = HKSampleType.categoryType(forIdentifier: .headache)! - + headacheType = Set([ HKSampleType.categoryType(forIdentifier: .headache)! ]) } - + if #available(iOS 14.0, *) { dataTypesDict[ELECTROCARDIOGRAM] = HKSampleType.electrocardiogramType() - + unitDict[VOLT] = HKUnit.volt() unitDict[INCHES_OF_MERCURY] = HKUnit.inchesOfMercury() - + workoutActivityTypeMap["CARDIO_DANCE"] = HKWorkoutActivityType.cardioDance workoutActivityTypeMap["SOCIAL_DANCE"] = HKWorkoutActivityType.socialDance workoutActivityTypeMap["PICKLEBALL"] = HKWorkoutActivityType.pickleball @@ -1614,13 +1614,13 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { if #available(iOS 16.0, *) { dataTypesDict[ATRIAL_FIBRILLATION_BURDEN] = HKQuantityType.quantityType(forIdentifier: .atrialFibrillationBurden)! - } - + } + // Concatenate heart events, headache and health data types (both may be empty) allDataTypes = Set(heartRateEventTypes + healthDataTypes) allDataTypes = allDataTypes.union(headacheType) } - + func getWorkoutType(type: HKWorkoutActivityType) -> String { switch type { case .americanFootball: From 289646bfb51771474dd2ae62154783f0310ef8a4 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Sat, 4 Jan 2025 21:51:21 +0100 Subject: [PATCH 60/80] Update CHANGELOG for version 12.0.0 --- packages/health/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index d1e668fd7..3ebf9db01 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,3 +1,8 @@ +## 12.0.0 + +* Fix of [#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) + ## 11.1.1 * Fix of [#1059](https://github.com/cph-cachet/flutter-plugins/issues/1059) From dcdba9e72632873332d572b6db507516097f8fcb Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Sat, 4 Jan 2025 22:01:42 +0100 Subject: [PATCH 61/80] Update dependencies: intl to ^0.20.1, device_info_plus to ^11.2.0, and permission_handler to ^11.3.1 --- packages/health/CHANGELOG.md | 3 +++ packages/health/example/pubspec.yaml | 2 +- packages/health/pubspec.yaml | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 3ebf9db01..9d1a0c4e7 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -2,6 +2,9 @@ * Fix of [#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) +* Updated `intl` to ^0.20.1 - Closes [#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 diff --git a/packages/health/example/pubspec.yaml b/packages/health/example/pubspec.yaml index d472b3f03..031bcc6c2 100644 --- a/packages/health/example/pubspec.yaml +++ b/packages/health/example/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.2 - permission_handler: ^10.2.0 + permission_handler: ^11.3.1 carp_serializable: ^2.0.0 # polymorphic json serialization health: path: ../ diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index a97aeaa6f..30318d95d 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -10,8 +10,8 @@ environment: dependencies: flutter: sdk: flutter - intl: '>=0.18.0 <0.20.0' - device_info_plus: '>=9.0.0 <11.0.0' + intl: ^0.20.1 + device_info_plus: ^11.2.0 json_annotation: ^4.8.0 carp_serializable: ^2.0.0 # polymorphic json serialization From dd5d5f905481b77a3899000520d977b763530423 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Sun, 5 Jan 2025 03:41:18 +0100 Subject: [PATCH 62/80] Fix null handling for record name in HealthPlugin --- .../src/main/kotlin/cachet/plugins/health/HealthPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt index 5c261f90f..94c0fce99 100644 --- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt +++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt @@ -1516,7 +1516,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] From 7a98254dbedcbe9237308556bfd9ef69f84bc4b3 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Sun, 5 Jan 2025 03:48:13 +0100 Subject: [PATCH 63/80] Update CHANGELOG.md to include fixes for issues #950 and #1104 --- packages/health/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 9d1a0c4e7..1917bf296 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -2,6 +2,7 @@ * Fix of [#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 of [#950](https://github.com/cph-cachet/flutter-plugins/issues/950) - PR [#1103](https://github.com/cph-cachet/flutter-plugins/pull/1103) * Updated `intl` to ^0.20.1 - Closes [#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 From f9e86e88d1168f56aaf896b76f7e98a3a404c45d Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Sun, 5 Jan 2025 16:36:42 +0100 Subject: [PATCH 64/80] Add lean body mass data type and permissions; update example app --- packages/health/CHANGELOG.md | 2 +- .../android/app/src/main/AndroidManifest.xml | 2 ++ packages/health/example/lib/main.dart | 16 +++++++++++++++- packages/health/example/lib/util.dart | 2 ++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 96adb741a..8164c7d4d 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,6 +1,6 @@ ## 12.0.0 -* Add lean mass data type [#1078](https://github.com/cph-cachet/flutter-plugins/issues/1078) +* Add lean mass data type [#1078](https://github.com/cph-cachet/flutter-plugins/issues/1078) - PR [#1097](https://github.com/cph-cachet/flutter-plugins/pull/1097) * Fix of [#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 of [#950](https://github.com/cph-cachet/flutter-plugins/issues/950) - PR [#1103](https://github.com/cph-cachet/flutter-plugins/pull/1103) diff --git a/packages/health/example/android/app/src/main/AndroidManifest.xml b/packages/health/example/android/app/src/main/AndroidManifest.xml index f6d185ace..996adaf97 100644 --- a/packages/health/example/android/app/src/main/AndroidManifest.xml +++ b/packages/health/example/android/app/src/main/AndroidManifest.xml @@ -65,6 +65,8 @@ + + diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index b77eae30e..00ac1123a 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -178,7 +178,15 @@ class _HealthAppState extends State { _healthDataList = Health().removeDuplicates(_healthDataList); for (var data in _healthDataList) { - debugPrint(toJsonString(data)); + try { + // print the data points to the console + debugPrint(toJsonString(data)); + } catch (error) { + // FIXME: Getting json failed since its instance of 'HealthDataPoint' + debugPrint("Exception in printDataPoint: $error"); + // raise the error to stop the app from crashing + rethrow; + } } // update the UI to display the results @@ -285,6 +293,12 @@ class _HealthAppState extends State { type: HealthDataType.SLEEP_DEEP, startTime: earlier, endTime: now); + success &= await Health().writeHealthData( + value: 22, + type: HealthDataType.LEAN_BODY_MASS, + startTime: earlier, + endTime: now, + ); // specialized write methods success &= await Health().writeBloodOxygen( diff --git a/packages/health/example/lib/util.dart b/packages/health/example/lib/util.dart index e1c49101d..da7e28089 100644 --- a/packages/health/example/lib/util.dart +++ b/packages/health/example/lib/util.dart @@ -44,6 +44,7 @@ const List dataTypesIOS = [ HealthDataType.HEADACHE_MODERATE, HealthDataType.HEADACHE_SEVERE, HealthDataType.HEADACHE_UNSPECIFIED, + HealthDataType.LEAN_BODY_MASS, // note that a phone cannot write these ECG-based types - only read them // HealthDataType.ELECTROCARDIOGRAM, @@ -76,6 +77,7 @@ const List dataTypesAndroid = [ HealthDataType.BODY_FAT_PERCENTAGE, HealthDataType.HEIGHT, HealthDataType.WEIGHT, + HealthDataType.LEAN_BODY_MASS, // HealthDataType.BODY_MASS_INDEX, HealthDataType.BODY_TEMPERATURE, HealthDataType.HEART_RATE, From b6a9a4f3dfafdf61a7e25a2d79b8b9b0e47ef9c8 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Sun, 5 Jan 2025 16:48:30 +0100 Subject: [PATCH 65/80] Update CHANGELOG.md with AndroidManifest permissions for LEAN_BODY_MASS --- packages/health/CHANGELOG.md | 5 +++ .../ios/Runner.xcodeproj/project.pbxproj | 34 +++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 8164c7d4d..db6c8fbfa 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,6 +1,11 @@ ## 12.0.0 * Add lean 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 + + + ``` * Fix of [#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 of [#950](https://github.com/cph-cachet/flutter-plugins/issues/950) - PR [#1103](https://github.com/cph-cachet/flutter-plugins/pull/1103) diff --git a/packages/health/example/ios/Runner.xcodeproj/project.pbxproj b/packages/health/example/ios/Runner.xcodeproj/project.pbxproj index 4ce55d34f..794fdbba4 100644 --- a/packages/health/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/health/example/ios/Runner.xcodeproj/project.pbxproj @@ -155,6 +155,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 8AB8966E9F27B6C816D51EA9 /* [CP] Embed Pods Frameworks */, + FEE6262278064D703A8D8D21 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -293,6 +294,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 */ @@ -394,7 +413,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", @@ -531,7 +553,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", @@ -564,7 +589,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", From 54b8fbff949e40b15b2cfdd0119844d1b957e8b9 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:56:44 +0100 Subject: [PATCH 66/80] Add support for WaterTemperature and UnderwaterDepth data types and UnderwaterDiving workout in iOS --- packages/health/CHANGELOG.md | 2 + packages/health/README.md | 4 + .../ios/Runner.xcodeproj/project.pbxproj | 34 +- packages/health/example/lib/main.dart | 19 + packages/health/example/lib/util.dart | 2 + .../ios/Classes/SwiftHealthPlugin.swift | 14 +- packages/health/ios/health.podspec | 4 +- packages/health/lib/health.g.dart | 343 +++++++----------- packages/health/lib/src/health_plugin.dart | 1 + packages/health/lib/src/heath_data_types.dart | 7 + 10 files changed, 219 insertions(+), 211 deletions(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 1917bf296..c07bafbf5 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -6,6 +6,8 @@ * Updated `intl` to ^0.20.1 - Closes [#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 +* iOS: Add `WATER_TEMPERATURE` and `UNDERWATER_DEPTH` health values - Closes [#1096](https://github.com/cph-cachet/flutter-plugins/issues/1096) +* iOS: Add support for `Underwater Diving` workout - Closes [#1096](https://github.com/cph-cachet/flutter-plugins/issues/1096) ## 11.1.1 diff --git a/packages/health/README.md b/packages/health/README.md index 68ec291bc..e10b90ab5 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -355,6 +355,9 @@ 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 | ## Workout Types @@ -443,6 +446,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 | | | diff --git a/packages/health/example/ios/Runner.xcodeproj/project.pbxproj b/packages/health/example/ios/Runner.xcodeproj/project.pbxproj index 4ce55d34f..a64ef83b6 100644 --- a/packages/health/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/health/example/ios/Runner.xcodeproj/project.pbxproj @@ -155,6 +155,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 8AB8966E9F27B6C816D51EA9 /* [CP] Embed Pods Frameworks */, + 99FD4B47838A33EC942FDC35 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -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; @@ -394,7 +413,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", @@ -531,7 +553,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", @@ -564,7 +589,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", diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index b77eae30e..e02c1e037 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -376,6 +376,25 @@ class _HealthAppState extends State { endTime: now, ); + // Available on iOS 16.0+ only + if (Platform.isIOS) { + success &= await Health().writeHealthData( + value: 22, + type: HealthDataType.WATER_TEMPERATURE, + startTime: earlier, + endTime: now, + recordingMethod: RecordingMethod.manual + ); + + success &= await Health().writeHealthData( + value: 55, + type: HealthDataType.UNDERWATER_DEPTH, + startTime: earlier, + endTime: now, + recordingMethod: RecordingMethod.manual + ); + } + setState(() { _state = success ? AppState.DATA_ADDED : AppState.DATA_NOT_ADDED; }); diff --git a/packages/health/example/lib/util.dart b/packages/health/example/lib/util.dart index e1c49101d..24168041f 100644 --- a/packages/health/example/lib/util.dart +++ b/packages/health/example/lib/util.dart @@ -59,6 +59,8 @@ const List dataTypesIOS = [ HealthDataType.BLOOD_TYPE, HealthDataType.BIRTH_DATE, HealthDataType.MENSTRUATION_FLOW, + HealthDataType.WATER_TEMPERATURE, + HealthDataType.UNDERWATER_DEPTH, ]; /// List of data types available on Android. diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift index a0bb9d4fd..ebb1f5d40 100644 --- a/packages/health/ios/Classes/SwiftHealthPlugin.swift +++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift @@ -161,6 +161,8 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { let GENDER = "GENDER" let BLOOD_TYPE = "BLOOD_TYPE" let MENSTRUATION_FLOW = "MENSTRUATION_FLOW" + let WATER_TEMPERATURE = "WATER_TEMPERATURE" + let UNDERWATER_DEPTH = "UNDERWATER_DEPTH" // Health Unit types @@ -857,7 +859,8 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { "recording_method": (sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool == true) ? RecordingMethod.manual.rawValue : RecordingMethod.automatic.rawValue, - "metadata": dataTypeKey == INSULIN_DELIVERY ? sample.metadata : nil + "metadata": dataTypeKey == INSULIN_DELIVERY ? sample.metadata : nil, + "dataUnitKey": unit?.unitString ] } DispatchQueue.main.async { @@ -1384,6 +1387,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { workoutActivityTypeMap["TAI_CHI"] = .taiChi workoutActivityTypeMap["WRESTLING"] = .wrestling workoutActivityTypeMap["OTHER"] = .other + if #available(iOS 17.0, *) { + workoutActivityTypeMap["UNDERWATER_DIVING"] = .underwaterDiving + } + nutritionList = [ DIETARY_ENERGY_CONSUMED, DIETARY_CARBS_CONSUMED, DIETARY_PROTEIN_CONSUMED, DIETARY_FATS_CONSUMED, DIETARY_CAFFEINE, DIETARY_FIBER, DIETARY_SUGAR, @@ -1617,6 +1624,9 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { if #available(iOS 16.0, *) { dataTypesDict[ATRIAL_FIBRILLATION_BURDEN] = HKQuantityType.quantityType(forIdentifier: .atrialFibrillationBurden)! + + dataTypesDict[WATER_TEMPERATURE] = HKQuantityType.quantityType(forIdentifier: .waterTemperature)! + dataTypesDict[UNDERWATER_DEPTH] = HKQuantityType.quantityType(forIdentifier: .underwaterDepth)! } // Concatenate heart events, headache and health data types (both may be empty) @@ -1774,6 +1784,8 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin { return "mixedCardio" case .handCycling: return "handCycling" + case .underwaterDiving: + return "underwaterDiving" default: return "other" } diff --git a/packages/health/ios/health.podspec b/packages/health/ios/health.podspec index 3a8d89370..96254602b 100644 --- a/packages/health/ios/health.podspec +++ b/packages/health/ios/health.podspec @@ -3,14 +3,14 @@ # Pod::Spec.new do |s| s.name = 'health' - s.version = '1.0.4' + s.version = '12.0.0' s.summary = 'Wrapper for Apple\'s HealthKit on iOS and Google\'s Health Connect on Android.' s.description = <<-DESC Wrapper for Apple's HealthKit on iOS and Google's Health Connect on Android. DESC s.homepage = 'https://pub.dev/packages/health' s.license = { :file => '../LICENSE' } - s.author = { 'Copenhagen Center for Health Technology' => 'cph.cachet@gmail.com' } + s.author = { 'Copenhagen Research Platform at DTU' => 'support@carp.dk' } s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart index fa4423446..c61f0d2e5 100644 --- a/packages/health/lib/health.g.dart +++ b/packages/health/lib/health.g.dart @@ -29,31 +29,23 @@ HealthDataPoint _$HealthDataPointFromJson(Map json) => metadata: json['metadata'] as Map?, ); -Map _$HealthDataPointToJson(HealthDataPoint instance) { - final val = { - 'uuid': instance.uuid, - 'value': instance.value.toJson(), - 'type': _$HealthDataTypeEnumMap[instance.type]!, - 'unit': _$HealthDataUnitEnumMap[instance.unit]!, - 'dateFrom': instance.dateFrom.toIso8601String(), - 'dateTo': instance.dateTo.toIso8601String(), - 'sourcePlatform': _$HealthPlatformTypeEnumMap[instance.sourcePlatform]!, - 'sourceDeviceId': instance.sourceDeviceId, - 'sourceId': instance.sourceId, - 'sourceName': instance.sourceName, - 'recordingMethod': _$RecordingMethodEnumMap[instance.recordingMethod]!, - }; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('workoutSummary', instance.workoutSummary?.toJson()); - writeNotNull('metadata', instance.metadata); - return val; -} +Map _$HealthDataPointToJson(HealthDataPoint instance) => + { + 'uuid': instance.uuid, + 'value': instance.value.toJson(), + 'type': _$HealthDataTypeEnumMap[instance.type]!, + 'unit': _$HealthDataUnitEnumMap[instance.unit]!, + 'dateFrom': instance.dateFrom.toIso8601String(), + 'dateTo': instance.dateTo.toIso8601String(), + 'sourcePlatform': _$HealthPlatformTypeEnumMap[instance.sourcePlatform]!, + 'sourceDeviceId': instance.sourceDeviceId, + 'sourceId': instance.sourceId, + 'sourceName': instance.sourceName, + 'recordingMethod': _$RecordingMethodEnumMap[instance.recordingMethod]!, + if (instance.workoutSummary?.toJson() case final value?) + 'workoutSummary': value, + if (instance.metadata case final value?) 'metadata': value, + }; const _$HealthDataTypeEnumMap = { HealthDataType.ACTIVE_ENERGY_BURNED: 'ACTIVE_ENERGY_BURNED', @@ -148,6 +140,8 @@ const _$HealthDataTypeEnumMap = { HealthDataType.BIRTH_DATE: 'BIRTH_DATE', HealthDataType.BLOOD_TYPE: 'BLOOD_TYPE', HealthDataType.MENSTRUATION_FLOW: 'MENSTRUATION_FLOW', + HealthDataType.WATER_TEMPERATURE: 'WATER_TEMPERATURE', + HealthDataType.UNDERWATER_DEPTH: 'UNDERWATER_DEPTH', HealthDataType.HIGH_HEART_RATE_EVENT: 'HIGH_HEART_RATE_EVENT', HealthDataType.LOW_HEART_RATE_EVENT: 'LOW_HEART_RATE_EVENT', HealthDataType.IRREGULAR_HEART_RATE_EVENT: 'IRREGULAR_HEART_RATE_EVENT', @@ -223,37 +217,21 @@ const _$RecordingMethodEnumMap = { HealthValue _$HealthValueFromJson(Map json) => HealthValue()..$type = json['__type'] as String?; -Map _$HealthValueToJson(HealthValue instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('__type', instance.$type); - return val; -} +Map _$HealthValueToJson(HealthValue instance) => + { + if (instance.$type case final value?) '__type': value, + }; NumericHealthValue _$NumericHealthValueFromJson(Map json) => NumericHealthValue( numericValue: json['numericValue'] as num, )..$type = json['__type'] as String?; -Map _$NumericHealthValueToJson(NumericHealthValue instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('__type', instance.$type); - val['numericValue'] = instance.numericValue; - return val; -} +Map _$NumericHealthValueToJson(NumericHealthValue instance) => + { + if (instance.$type case final value?) '__type': value, + 'numericValue': instance.numericValue, + }; AudiogramHealthValue _$AudiogramHealthValueFromJson( Map json) => @@ -269,21 +247,13 @@ AudiogramHealthValue _$AudiogramHealthValueFromJson( )..$type = json['__type'] as String?; Map _$AudiogramHealthValueToJson( - AudiogramHealthValue instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('__type', instance.$type); - val['frequencies'] = instance.frequencies; - val['leftEarSensitivities'] = instance.leftEarSensitivities; - val['rightEarSensitivities'] = instance.rightEarSensitivities; - return val; -} + AudiogramHealthValue instance) => + { + if (instance.$type case final value?) '__type': value, + 'frequencies': instance.frequencies, + 'leftEarSensitivities': instance.leftEarSensitivities, + 'rightEarSensitivities': instance.rightEarSensitivities, + }; WorkoutHealthValue _$WorkoutHealthValueFromJson(Map json) => WorkoutHealthValue( @@ -300,29 +270,23 @@ WorkoutHealthValue _$WorkoutHealthValueFromJson(Map json) => $enumDecodeNullable(_$HealthDataUnitEnumMap, json['totalStepsUnit']), )..$type = json['__type'] as String?; -Map _$WorkoutHealthValueToJson(WorkoutHealthValue instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('__type', instance.$type); - val['workoutActivityType'] = - _$HealthWorkoutActivityTypeEnumMap[instance.workoutActivityType]!; - writeNotNull('totalEnergyBurned', instance.totalEnergyBurned); - writeNotNull('totalEnergyBurnedUnit', - _$HealthDataUnitEnumMap[instance.totalEnergyBurnedUnit]); - writeNotNull('totalDistance', instance.totalDistance); - writeNotNull( - 'totalDistanceUnit', _$HealthDataUnitEnumMap[instance.totalDistanceUnit]); - writeNotNull('totalSteps', instance.totalSteps); - writeNotNull( - 'totalStepsUnit', _$HealthDataUnitEnumMap[instance.totalStepsUnit]); - return val; -} +Map _$WorkoutHealthValueToJson(WorkoutHealthValue instance) => + { + if (instance.$type case final value?) '__type': value, + 'workoutActivityType': + _$HealthWorkoutActivityTypeEnumMap[instance.workoutActivityType]!, + if (instance.totalEnergyBurned case final value?) + 'totalEnergyBurned': value, + if (_$HealthDataUnitEnumMap[instance.totalEnergyBurnedUnit] + case final value?) + 'totalEnergyBurnedUnit': value, + if (instance.totalDistance case final value?) 'totalDistance': value, + if (_$HealthDataUnitEnumMap[instance.totalDistanceUnit] case final value?) + 'totalDistanceUnit': value, + if (instance.totalSteps case final value?) 'totalSteps': value, + if (_$HealthDataUnitEnumMap[instance.totalStepsUnit] case final value?) + 'totalStepsUnit': value, + }; const _$HealthWorkoutActivityTypeEnumMap = { HealthWorkoutActivityType.AMERICAN_FOOTBALL: 'AMERICAN_FOOTBALL', @@ -406,6 +370,7 @@ const _$HealthWorkoutActivityTypeEnumMap = { HealthWorkoutActivityType.WHEELCHAIR_RUN_PACE: 'WHEELCHAIR_RUN_PACE', HealthWorkoutActivityType.WHEELCHAIR_WALK_PACE: 'WHEELCHAIR_WALK_PACE', HealthWorkoutActivityType.WRESTLING: 'WRESTLING', + HealthWorkoutActivityType.UNDERWATER_DIVING: 'UNDERWATER_DIVING', HealthWorkoutActivityType.BIKING_STATIONARY: 'BIKING_STATIONARY', HealthWorkoutActivityType.CALISTHENICS: 'CALISTHENICS', HealthWorkoutActivityType.DANCING: 'DANCING', @@ -443,23 +408,18 @@ ElectrocardiogramHealthValue _$ElectrocardiogramHealthValueFromJson( )..$type = json['__type'] as String?; Map _$ElectrocardiogramHealthValueToJson( - ElectrocardiogramHealthValue instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('__type', instance.$type); - val['voltageValues'] = instance.voltageValues.map((e) => e.toJson()).toList(); - writeNotNull('averageHeartRate', instance.averageHeartRate); - writeNotNull('samplingFrequency', instance.samplingFrequency); - writeNotNull('classification', - _$ElectrocardiogramClassificationEnumMap[instance.classification]); - return val; -} + ElectrocardiogramHealthValue instance) => + { + if (instance.$type case final value?) '__type': value, + 'voltageValues': instance.voltageValues.map((e) => e.toJson()).toList(), + if (instance.averageHeartRate case final value?) + 'averageHeartRate': value, + if (instance.samplingFrequency case final value?) + 'samplingFrequency': value, + if (_$ElectrocardiogramClassificationEnumMap[instance.classification] + case final value?) + 'classification': value, + }; const _$ElectrocardiogramClassificationEnumMap = { ElectrocardiogramClassification.NOT_SET: 'NOT_SET', @@ -483,20 +443,12 @@ ElectrocardiogramVoltageValue _$ElectrocardiogramVoltageValueFromJson( )..$type = json['__type'] as String?; Map _$ElectrocardiogramVoltageValueToJson( - ElectrocardiogramVoltageValue instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('__type', instance.$type); - val['voltage'] = instance.voltage; - val['timeSinceSampleStart'] = instance.timeSinceSampleStart; - return val; -} + ElectrocardiogramVoltageValue instance) => + { + if (instance.$type case final value?) '__type': value, + 'voltage': instance.voltage, + 'timeSinceSampleStart': instance.timeSinceSampleStart, + }; InsulinDeliveryHealthValue _$InsulinDeliveryHealthValueFromJson( Map json) => @@ -506,20 +458,12 @@ InsulinDeliveryHealthValue _$InsulinDeliveryHealthValueFromJson( )..$type = json['__type'] as String?; Map _$InsulinDeliveryHealthValueToJson( - InsulinDeliveryHealthValue instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('__type', instance.$type); - val['units'] = instance.units; - val['reason'] = _$InsulinDeliveryReasonEnumMap[instance.reason]!; - return val; -} + InsulinDeliveryHealthValue instance) => + { + if (instance.$type case final value?) '__type': value, + 'units': instance.units, + 'reason': _$InsulinDeliveryReasonEnumMap[instance.reason]!, + }; const _$InsulinDeliveryReasonEnumMap = { InsulinDeliveryReason.NOT_SET: 'NOT_SET', @@ -577,62 +521,58 @@ NutritionHealthValue _$NutritionHealthValueFromJson( )..$type = json['__type'] as String?; Map _$NutritionHealthValueToJson( - NutritionHealthValue instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('__type', instance.$type); - writeNotNull('name', instance.name); - writeNotNull('mealType', instance.mealType); - writeNotNull('calories', instance.calories); - writeNotNull('protein', instance.protein); - writeNotNull('fat', instance.fat); - writeNotNull('carbs', instance.carbs); - writeNotNull('caffeine', instance.caffeine); - writeNotNull('vitaminA', instance.vitaminA); - writeNotNull('b1Thiamine', instance.b1Thiamine); - writeNotNull('b2Riboflavin', instance.b2Riboflavin); - writeNotNull('b3Niacin', instance.b3Niacin); - writeNotNull('b5PantothenicAcid', instance.b5PantothenicAcid); - writeNotNull('b6Pyridoxine', instance.b6Pyridoxine); - writeNotNull('b7Biotin', instance.b7Biotin); - writeNotNull('b9Folate', instance.b9Folate); - writeNotNull('b12Cobalamin', instance.b12Cobalamin); - writeNotNull('vitaminC', instance.vitaminC); - writeNotNull('vitaminD', instance.vitaminD); - writeNotNull('vitaminE', instance.vitaminE); - writeNotNull('vitaminK', instance.vitaminK); - writeNotNull('calcium', instance.calcium); - writeNotNull('chloride', instance.chloride); - writeNotNull('cholesterol', instance.cholesterol); - writeNotNull('choline', instance.choline); - writeNotNull('chromium', instance.chromium); - writeNotNull('copper', instance.copper); - writeNotNull('fatUnsaturated', instance.fatUnsaturated); - writeNotNull('fatMonounsaturated', instance.fatMonounsaturated); - writeNotNull('fatPolyunsaturated', instance.fatPolyunsaturated); - writeNotNull('fatSaturated', instance.fatSaturated); - writeNotNull('fatTransMonoenoic', instance.fatTransMonoenoic); - writeNotNull('fiber', instance.fiber); - writeNotNull('iodine', instance.iodine); - writeNotNull('iron', instance.iron); - writeNotNull('magnesium', instance.magnesium); - writeNotNull('manganese', instance.manganese); - writeNotNull('molybdenum', instance.molybdenum); - writeNotNull('phosphorus', instance.phosphorus); - writeNotNull('potassium', instance.potassium); - writeNotNull('selenium', instance.selenium); - writeNotNull('sodium', instance.sodium); - writeNotNull('sugar', instance.sugar); - writeNotNull('water', instance.water); - writeNotNull('zinc', instance.zinc); - return val; -} + NutritionHealthValue instance) => + { + if (instance.$type case final value?) '__type': value, + if (instance.name case final value?) 'name': value, + if (instance.mealType case final value?) 'mealType': value, + if (instance.calories case final value?) 'calories': value, + if (instance.protein case final value?) 'protein': value, + if (instance.fat case final value?) 'fat': value, + if (instance.carbs case final value?) 'carbs': value, + if (instance.caffeine case final value?) 'caffeine': value, + if (instance.vitaminA case final value?) 'vitaminA': value, + if (instance.b1Thiamine case final value?) 'b1Thiamine': value, + if (instance.b2Riboflavin case final value?) 'b2Riboflavin': value, + if (instance.b3Niacin case final value?) 'b3Niacin': value, + if (instance.b5PantothenicAcid case final value?) + 'b5PantothenicAcid': value, + if (instance.b6Pyridoxine case final value?) 'b6Pyridoxine': value, + if (instance.b7Biotin case final value?) 'b7Biotin': value, + if (instance.b9Folate case final value?) 'b9Folate': value, + if (instance.b12Cobalamin case final value?) 'b12Cobalamin': value, + if (instance.vitaminC case final value?) 'vitaminC': value, + if (instance.vitaminD case final value?) 'vitaminD': value, + if (instance.vitaminE case final value?) 'vitaminE': value, + if (instance.vitaminK case final value?) 'vitaminK': value, + if (instance.calcium case final value?) 'calcium': value, + if (instance.chloride case final value?) 'chloride': value, + if (instance.cholesterol case final value?) 'cholesterol': value, + if (instance.choline case final value?) 'choline': value, + if (instance.chromium case final value?) 'chromium': value, + if (instance.copper case final value?) 'copper': value, + if (instance.fatUnsaturated case final value?) 'fatUnsaturated': value, + if (instance.fatMonounsaturated case final value?) + 'fatMonounsaturated': value, + if (instance.fatPolyunsaturated case final value?) + 'fatPolyunsaturated': value, + if (instance.fatSaturated case final value?) 'fatSaturated': value, + if (instance.fatTransMonoenoic case final value?) + 'fatTransMonoenoic': value, + if (instance.fiber case final value?) 'fiber': value, + if (instance.iodine case final value?) 'iodine': value, + if (instance.iron case final value?) 'iron': value, + if (instance.magnesium case final value?) 'magnesium': value, + if (instance.manganese case final value?) 'manganese': value, + if (instance.molybdenum case final value?) 'molybdenum': value, + if (instance.phosphorus case final value?) 'phosphorus': value, + if (instance.potassium case final value?) 'potassium': value, + if (instance.selenium case final value?) 'selenium': value, + if (instance.sodium case final value?) 'sodium': value, + if (instance.sugar case final value?) 'sugar': value, + if (instance.water case final value?) 'water': value, + if (instance.zinc case final value?) 'zinc': value, + }; MenstruationFlowHealthValue _$MenstruationFlowHealthValueFromJson( Map json) => @@ -644,22 +584,15 @@ MenstruationFlowHealthValue _$MenstruationFlowHealthValueFromJson( )..$type = json['__type'] as String?; Map _$MenstruationFlowHealthValueToJson( - MenstruationFlowHealthValue instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('__type', instance.$type); - writeNotNull('flow', _$MenstrualFlowEnumMap[instance.flow]); - writeNotNull('isStartOfCycle', instance.isStartOfCycle); - writeNotNull('wasUserEntered', instance.wasUserEntered); - val['dateTime'] = instance.dateTime.toIso8601String(); - return val; -} + MenstruationFlowHealthValue instance) => + { + if (instance.$type case final value?) '__type': value, + if (_$MenstrualFlowEnumMap[instance.flow] case final value?) + 'flow': value, + if (instance.isStartOfCycle case final value?) 'isStartOfCycle': value, + if (instance.wasUserEntered case final value?) 'wasUserEntered': value, + 'dateTime': instance.dateTime.toIso8601String(), + }; const _$MenstrualFlowEnumMap = { MenstrualFlow.unspecified: 'unspecified', diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 8868a519b..6f513b0ac 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -1257,6 +1257,7 @@ class Health { HealthWorkoutActivityType.YOGA, HealthWorkoutActivityType.SWIMMING_OPEN_WATER, HealthWorkoutActivityType.SWIMMING_POOL, + HealthWorkoutActivityType.UNDERWATER_DIVING, }.contains(type); } diff --git a/packages/health/lib/src/heath_data_types.dart b/packages/health/lib/src/heath_data_types.dart index f50762721..878bbc00f 100644 --- a/packages/health/lib/src/heath_data_types.dart +++ b/packages/health/lib/src/heath_data_types.dart @@ -95,6 +95,8 @@ enum HealthDataType { BIRTH_DATE, BLOOD_TYPE, MENSTRUATION_FLOW, + WATER_TEMPERATURE, + UNDERWATER_DEPTH, // Heart Rate events (specific to Apple Watch) HIGH_HEART_RATE_EVENT, @@ -206,6 +208,8 @@ const List dataTypeKeysIOS = [ HealthDataType.BIRTH_DATE, HealthDataType.BLOOD_TYPE, HealthDataType.MENSTRUATION_FLOW, + HealthDataType.WATER_TEMPERATURE, + HealthDataType.UNDERWATER_DEPTH, ]; /// List of data types available on Android @@ -352,6 +356,8 @@ const Map dataTypeToUnit = { HealthDataType.NUTRITION: HealthDataUnit.NO_UNIT, HealthDataType.MENSTRUATION_FLOW: HealthDataUnit.NO_UNIT, + HealthDataType.WATER_TEMPERATURE: HealthDataUnit.DEGREE_CELSIUS, + HealthDataType.UNDERWATER_DEPTH: HealthDataUnit.METER, // Health Connect HealthDataType.TOTAL_CALORIES_BURNED: HealthDataUnit.KILOCALORIE, @@ -527,6 +533,7 @@ enum HealthWorkoutActivityType { WHEELCHAIR_RUN_PACE, WHEELCHAIR_WALK_PACE, WRESTLING, + UNDERWATER_DIVING, // Android only BIKING_STATIONARY, From ecd36d41c9f3d270b94ca82077ca9234a3da6c11 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:42:13 +0100 Subject: [PATCH 67/80] Update build for LEAN_BODY_MASS data type --- packages/health/CHANGELOG.md | 12 ++++++------ packages/health/README.md | 1 + packages/health/example/lib/main.dart | 10 +--------- packages/health/lib/health.g.dart | 1 + 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 6dd9d146b..093d10da9 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,19 +1,19 @@ ## 12.0.0 -* Add lean mass data type [#1078](https://github.com/cph-cachet/flutter-plugins/issues/1078) - PR [#1097](https://github.com/cph-cachet/flutter-plugins/pull/1097) +* 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 ``` -* Fix of [#1072](https://github.com/cph-cachet/flutter-plugins/issues/1072) and [#1074](https://github.com/cph-cachet/flutter-plugins/issues/1074) +* 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 of [#950](https://github.com/cph-cachet/flutter-plugins/issues/950) - PR [#1103](https://github.com/cph-cachet/flutter-plugins/pull/1103) -* Updated `intl` to ^0.20.1 - Closes [#1092](https://github.com/cph-cachet/flutter-plugins/issues/1092) +* Fix [#950](https://github.com/cph-cachet/flutter-plugins/issues/950) - PR [#1103](https://github.com/cph-cachet/flutter-plugins/pull/1103) +* 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 -* iOS: Add `WATER_TEMPERATURE` and `UNDERWATER_DEPTH` health values - Closes [#1096](https://github.com/cph-cachet/flutter-plugins/issues/1096) -* iOS: Add support for `Underwater Diving` workout - Closes [#1096](https://github.com/cph-cachet/flutter-plugins/issues/1096) ## 11.1.1 diff --git a/packages/health/README.md b/packages/health/README.md index e10b90ab5..eea60737c 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -358,6 +358,7 @@ The plugin supports the following [`HealthDataType`](https://pub.dev/documentati | 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 diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index 95b964061..e86985d6c 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -178,15 +178,7 @@ class _HealthAppState extends State { _healthDataList = Health().removeDuplicates(_healthDataList); for (var data in _healthDataList) { - try { - // print the data points to the console - debugPrint(toJsonString(data)); - } catch (error) { - // FIXME: Getting json failed since its instance of 'HealthDataPoint' - debugPrint("Exception in printDataPoint: $error"); - // raise the error to stop the app from crashing - rethrow; - } + debugPrint(toJsonString(data)); } // update the UI to display the results diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart index c61f0d2e5..70feb2de1 100644 --- a/packages/health/lib/health.g.dart +++ b/packages/health/lib/health.g.dart @@ -57,6 +57,7 @@ const _$HealthDataTypeEnumMap = { HealthDataType.BLOOD_PRESSURE_DIASTOLIC: 'BLOOD_PRESSURE_DIASTOLIC', HealthDataType.BLOOD_PRESSURE_SYSTOLIC: 'BLOOD_PRESSURE_SYSTOLIC', HealthDataType.BODY_FAT_PERCENTAGE: 'BODY_FAT_PERCENTAGE', + HealthDataType.LEAN_BODY_MASS: 'LEAN_BODY_MASS', HealthDataType.BODY_MASS_INDEX: 'BODY_MASS_INDEX', HealthDataType.BODY_TEMPERATURE: 'BODY_TEMPERATURE', HealthDataType.BODY_WATER_MASS: 'BODY_WATER_MASS', From a1ee70e854c690db56a10fe4b1250ecc5252db53 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Tue, 7 Jan 2025 16:06:23 +0100 Subject: [PATCH 68/80] Update CHANGELOG.md to include fixes for issues #1047 and #939 --- packages/health/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 093d10da9..27cfdc1d6 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -11,6 +11,7 @@ * 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) * 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 From 80af770feb0a7de676d0b2007e316366f3a629cc Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Tue, 7 Jan 2025 16:38:55 +0100 Subject: [PATCH 69/80] chore: update CHANGELOG.md for breaking change in permission requests for WORKOUT data type --- packages/health/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 27cfdc1d6..80d850a8a 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,5 +1,8 @@ ## 12.0.0 +* **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 From 86a8fac377b374b09ac62004984a440aee59e355 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Tue, 7 Jan 2025 16:49:45 +0100 Subject: [PATCH 70/80] chore: update CHANGELOG.md to include fix for SLEEP_LIGHT type alignment issue --- packages/health/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 80d850a8a..e6efc7c4e 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -15,6 +15,7 @@ * 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) * 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 From 35cd46c75b582881f0500d78450c6b4d098757e2 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:17:32 +0100 Subject: [PATCH 71/80] feat: refactor Health class to remove singleton pattern and enable dependency injection for DeviceInfoPlugin --- packages/health/CHANGELOG.md | 10 + packages/health/example/lib/main.dart | 85 ++--- packages/health/lib/src/health_plugin.dart | 16 +- packages/health/pubspec.yaml | 1 + packages/health/test/health_test.dart | 341 ++++++++++++++++++ .../health/test/mocks/device_info_mock.dart | 60 +++ 6 files changed, 462 insertions(+), 51 deletions(-) create mode 100644 packages/health/test/mocks/device_info_mock.dart diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index e6efc7c4e..994906485 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,5 +1,15 @@ ## 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, as explained in the **Initialization** section below. + - Previously, the `Health` class directly instantiated the `DeviceInfoPlugin` internally, which was not ideal when trying to mock this functionality in unit tests, and could lead to problems with state management. + * **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) diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index e86985d6c..f67f860a0 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -7,6 +7,9 @@ import 'package:health_example/util.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:carp_serializable/carp_serializable.dart'; +// Global Health instance +final health = Health(); + void main() => runApp(HealthApp()); class HealthApp extends StatefulWidget { @@ -85,15 +88,15 @@ class _HealthAppState extends State { @override void initState() { // configure the health plugin before use and check the Health Connect status - Health().configure(); - Health().getHealthConnectSdkStatus(); + health.configure(); + health.getHealthConnectSdkStatus(); super.initState(); } /// Install Google Health Connect on this phone. Future installHealthConnect() async => - await Health().installHealthConnect(); + await health.installHealthConnect(); /// Authorize, i.e. get permissions to access relevant health data. Future authorize() async { @@ -107,7 +110,7 @@ class _HealthAppState extends State { // Check if we have health permissions bool? hasPermissions = - await Health().hasPermissions(types, permissions: permissions); + await health.hasPermissions(types, permissions: permissions); // hasPermissions = false because the hasPermission cannot disclose if WRITE access exists. // Hence, we have to request with WRITE as well. @@ -117,8 +120,8 @@ class _HealthAppState extends State { if (!hasPermissions) { // requesting access to the data types before reading them try { - authorized = await Health() - .requestAuthorization(types, permissions: permissions); + authorized = + await health.requestAuthorization(types, permissions: permissions); } catch (error) { debugPrint("Exception in authorize: $error"); } @@ -132,7 +135,7 @@ class _HealthAppState extends State { Future getHealthConnectSdkStatus() async { assert(Platform.isAndroid, "This is only available on Android"); - final status = await Health().getHealthConnectSdkStatus(); + final status = await health.getHealthConnectSdkStatus(); setState(() { _contentHealthConnectStatus = @@ -154,7 +157,7 @@ class _HealthAppState extends State { try { // fetch health data - List healthData = await Health().getHealthDataFromTypes( + List healthData = await health.getHealthDataFromTypes( types: types, startTime: yesterday, endTime: now, @@ -175,7 +178,7 @@ class _HealthAppState extends State { } // filter out duplicates - _healthDataList = Health().removeDuplicates(_healthDataList); + _healthDataList = health.removeDuplicates(_healthDataList); for (var data in _healthDataList) { debugPrint(toJsonString(data)); @@ -201,91 +204,91 @@ class _HealthAppState extends State { bool success = true; // misc. health data examples using the writeHealthData() method - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 1.925, type: HealthDataType.HEIGHT, startTime: earlier, endTime: now, recordingMethod: RecordingMethod.manual); - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 90, type: HealthDataType.WEIGHT, startTime: now, recordingMethod: RecordingMethod.manual); - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 90, type: HealthDataType.HEART_RATE, startTime: earlier, endTime: now, recordingMethod: RecordingMethod.manual); - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 90, type: HealthDataType.STEPS, startTime: earlier, endTime: now, recordingMethod: RecordingMethod.manual); - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 200, type: HealthDataType.ACTIVE_ENERGY_BURNED, startTime: earlier, endTime: now, ); - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 70, type: HealthDataType.HEART_RATE, startTime: earlier, endTime: now); if (Platform.isIOS) { - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 30, type: HealthDataType.HEART_RATE_VARIABILITY_SDNN, startTime: earlier, endTime: now); } else { - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 30, type: HealthDataType.HEART_RATE_VARIABILITY_RMSSD, startTime: earlier, endTime: now); } - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 37, type: HealthDataType.BODY_TEMPERATURE, startTime: earlier, endTime: now); - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 105, type: HealthDataType.BLOOD_GLUCOSE, startTime: earlier, endTime: now); - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 1.8, type: HealthDataType.WATER, startTime: earlier, endTime: now); // different types of sleep - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 0.0, type: HealthDataType.SLEEP_REM, startTime: earlier, endTime: now); - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 0.0, type: HealthDataType.SLEEP_ASLEEP, startTime: earlier, endTime: now); - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 0.0, type: HealthDataType.SLEEP_AWAKE, startTime: earlier, endTime: now); - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 0.0, type: HealthDataType.SLEEP_DEEP, startTime: earlier, endTime: now); - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 22, type: HealthDataType.LEAN_BODY_MASS, startTime: earlier, @@ -293,12 +296,12 @@ class _HealthAppState extends State { ); // specialized write methods - success &= await Health().writeBloodOxygen( + success &= await health.writeBloodOxygen( saturation: 98, startTime: earlier, endTime: now, ); - success &= await Health().writeWorkoutData( + success &= await health.writeWorkoutData( activityType: HealthWorkoutActivityType.AMERICAN_FOOTBALL, title: "Random workout name that shows up in Health Connect", start: now.subtract(const Duration(minutes: 15)), @@ -306,12 +309,12 @@ class _HealthAppState extends State { totalDistance: 2430, totalEnergyBurned: 400, ); - success &= await Health().writeBloodPressure( + success &= await health.writeBloodPressure( systolic: 90, diastolic: 80, startTime: now, ); - success &= await Health().writeMeal( + success &= await health.writeMeal( mealType: MealType.SNACK, startTime: earlier, endTime: now, @@ -363,7 +366,7 @@ class _HealthAppState extends State { // const frequencies = [125.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0]; // const leftEarSensitivities = [49.0, 54.0, 89.0, 52.0, 77.0, 35.0]; // const rightEarSensitivities = [76.0, 66.0, 90.0, 22.0, 85.0, 44.5]; - // success &= await Health().writeAudiogram( + // success &= await health.writeAudiogram( // frequencies, // leftEarSensitivities, // rightEarSensitivities, @@ -375,7 +378,7 @@ class _HealthAppState extends State { // }, // ); - success &= await Health().writeMenstruationFlow( + success &= await health.writeMenstruationFlow( flow: MenstrualFlow.medium, isStartOfCycle: true, startTime: earlier, @@ -383,8 +386,8 @@ class _HealthAppState extends State { ); // Available on iOS 16.0+ only - if (Platform.isIOS) { - success &= await Health().writeHealthData( + if (Platform.isIOS) { + success &= await health.writeHealthData( value: 22, type: HealthDataType.WATER_TEMPERATURE, startTime: earlier, @@ -392,7 +395,7 @@ class _HealthAppState extends State { recordingMethod: RecordingMethod.manual ); - success &= await Health().writeHealthData( + success &= await health.writeHealthData( value: 55, type: HealthDataType.UNDERWATER_DEPTH, startTime: earlier, @@ -413,7 +416,7 @@ class _HealthAppState extends State { bool success = true; for (HealthDataType type in types) { - success &= await Health().delete( + success &= await health.delete( type: type, startTime: earlier, endTime: now, @@ -434,15 +437,15 @@ class _HealthAppState extends State { final midnight = DateTime(now.year, now.month, now.day); bool stepsPermission = - await Health().hasPermissions([HealthDataType.STEPS]) ?? false; + await health.hasPermissions([HealthDataType.STEPS]) ?? false; if (!stepsPermission) { stepsPermission = - await Health().requestAuthorization([HealthDataType.STEPS]); + await health.requestAuthorization([HealthDataType.STEPS]); } if (stepsPermission) { try { - steps = await Health().getTotalStepsInInterval(midnight, now, + steps = await health.getTotalStepsInInterval(midnight, now, includeManualEntry: !recordingMethodsToFilter.contains(RecordingMethod.manual)); } catch (error) { @@ -468,7 +471,7 @@ class _HealthAppState extends State { bool success = false; try { - await Health().revokePermissions(); + await health.revokePermissions(); success = true; } catch (error) { debugPrint("Exception in revokeAccess: $error"); @@ -503,7 +506,7 @@ class _HealthAppState extends State { child: const Text("Check Health Connect Status", style: TextStyle(color: Colors.white))), if (Platform.isAndroid && - Health().healthConnectSdkStatus != + health.healthConnectSdkStatus != HealthConnectSdkStatus.sdkAvailable) TextButton( onPressed: installHealthConnect, @@ -513,7 +516,7 @@ class _HealthAppState extends State { style: TextStyle(color: Colors.white))), if (Platform.isIOS || Platform.isAndroid && - Health().healthConnectSdkStatus == + health.healthConnectSdkStatus == HealthConnectSdkStatus.sdkAvailable) Wrap(spacing: 10, children: [ TextButton( diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 8df652e4e..06b76bb52 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -34,19 +34,15 @@ part of '../health.dart'; /// or getter methods. Otherwise, the plugin will throw an exception. class Health { static const MethodChannel _channel = MethodChannel('flutter_health'); - static final _instance = Health._(); String? _deviceId; - final _deviceInfo = DeviceInfoPlugin(); - HealthConnectSdkStatus _healthConnectSdkStatus = - HealthConnectSdkStatus.sdkUnavailable; + final DeviceInfoPlugin _deviceInfo; + HealthConnectSdkStatus _healthConnectSdkStatus = + HealthConnectSdkStatus.sdkUnavailable; - Health._() { - _registerFromJsonFunctions(); - } - - /// The singleton [Health] instance. - factory Health() => _instance; + Health({DeviceInfoPlugin? deviceInfo}) : _deviceInfo = deviceInfo ?? DeviceInfoPlugin() { + _registerFromJsonFunctions(); + } /// The latest status on availability of Health Connect SDK on this phone. HealthConnectSdkStatus get healthConnectSdkStatus => _healthConnectSdkStatus; diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index 8a1cc79f5..16c5823af 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -26,6 +26,7 @@ dev_dependencies: # dart run build_runner build --delete-conflicting-outputs build_runner: any json_serializable: any + mocktail: ^1.0.4 flutter: plugin: diff --git a/packages/health/test/health_test.dart b/packages/health/test/health_test.dart index 8b1378917..9e6759973 100644 --- a/packages/health/test/health_test.dart +++ b/packages/health/test/health_test.dart @@ -1 +1,342 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:health/health.dart'; +import 'dart:convert'; +import 'package:carp_serializable/carp_serializable.dart'; +import 'mocks/device_info_mock.dart'; // Import the mock file + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('HealthDataPoint fromJson Tests', () { + // Helper function to print the toJsonString, + // useful for debugging failed tests + String toJsonString(HealthDataPoint hdp) { + return jsonEncode(hdp.toJson()); + } + + //Instantiate Health class with the Mock + final health = Health(deviceInfo: MockDeviceInfoPlugin()); + setUpAll(() async { + await health.configure(); + }); + test('Test WorkoutHealthValue', () async { + var entry = { + "uuid": "A91A2F10-3D7B-486A-B140-5ADCD3C9C6D0", + "value": { + "__type": "WorkoutHealthValue", + "workoutActivityType": "AMERICAN_FOOTBALL", + "totalEnergyBurned": 100, + "totalEnergyBurnedUnit": "KILOCALORIE", + "totalDistance": 2000, + "totalDistanceUnit": "METER" + }, + "type": "WORKOUT", + "unit": "NO_UNIT", + "dateFrom": "2024-09-24T17:34:00.000", + "dateTo": "2024-09-24T17:57:00.000", + "sourcePlatform": "appleHealth", + "sourceDeviceId": "756B1A7A-C972-4BDB-9748-0D4749CF299C", + "sourceId": "com.apple.Health", + "sourceName": "Salud", + "recordingMethod": "manual", + "workoutSummary": { + "workoutType": "AMERICAN_FOOTBALL", + "totalDistance": 2000, + "totalEnergyBurned": 100, + "totalSteps": 0 + } + }; + + var hdp = HealthDataPoint.fromJson(entry); + + expect(hdp.uuid, "A91A2F10-3D7B-486A-B140-5ADCD3C9C6D0"); + expect(hdp.type, HealthDataType.WORKOUT); + expect(hdp.unit, HealthDataUnit.NO_UNIT); + expect(hdp.sourcePlatform, HealthPlatformType.appleHealth); + expect(hdp.sourceDeviceId, "756B1A7A-C972-4BDB-9748-0D4749CF299C"); + expect(hdp.sourceId, "com.apple.Health"); + expect(hdp.sourceName, "Salud"); + expect(hdp.recordingMethod, RecordingMethod.manual); + + expect(hdp.value, isA()); + expect((hdp.value as WorkoutHealthValue).workoutActivityType, + HealthWorkoutActivityType.AMERICAN_FOOTBALL); + expect((hdp.value as WorkoutHealthValue).totalEnergyBurned, 100); + expect((hdp.value as WorkoutHealthValue).totalEnergyBurnedUnit, + HealthDataUnit.KILOCALORIE); + expect((hdp.value as WorkoutHealthValue).totalDistance, 2000); + expect((hdp.value as WorkoutHealthValue).totalDistanceUnit, + HealthDataUnit.METER); + + // debugPrint(toJsonString(hdp)); + // don't print try to see if toJsonString works + expect(toJsonString(hdp), isA()); + + + }); + test('Test NumericHealthValue', () { + final json = { + "uuid": "some-uuid-1", + "value": {"__type": "NumericHealthValue", "numericValue": 123.45}, + "type": "HEART_RATE", + "unit": "COUNT", + "dateFrom": "2024-09-24T17:34:00.000", + "dateTo": "2024-09-24T17:57:00.000", + "sourcePlatform": "googleHealthConnect", + "sourceDeviceId": "some-device-id", + "sourceId": "some-source-id", + "sourceName": "some-source-name", + "recordingMethod": "automatic" + }; + + final hdp = HealthDataPoint.fromJson(json); + + expect(hdp.uuid, "some-uuid-1"); + expect(hdp.type, HealthDataType.HEART_RATE); + expect(hdp.unit, HealthDataUnit.COUNT); + expect(hdp.sourcePlatform, HealthPlatformType.googleHealthConnect); + expect(hdp.sourceDeviceId, "some-device-id"); + expect(hdp.sourceId, "some-source-id"); + expect(hdp.sourceName, "some-source-name"); + expect(hdp.recordingMethod, RecordingMethod.automatic); + + expect(hdp.value, isA()); + expect((hdp.value as NumericHealthValue).numericValue, 123.45); + // debugPrint(toJsonString(hdp)); + expect(toJsonString(hdp), isA()); + }); + test('Test AudiogramHealthValue', () { + final json = { + "uuid": "some-uuid-2", + "value": { + "__type": "AudiogramHealthValue", + "frequencies": [1000.0, 2000.0, 3000.0], + "leftEarSensitivities": [20.0, 25.0, 30.0], + "rightEarSensitivities": [15.0, 20.0, 25.0] + }, + "type": "AUDIOGRAM", + "unit": "DECIBEL_HEARING_LEVEL", + "dateFrom": "2024-09-24T17:34:00.000", + "dateTo": "2024-09-24T17:57:00.000", + "sourcePlatform": "appleHealth", + "sourceDeviceId": "some-device-id", + "sourceId": "some-source-id", + "sourceName": "some-source-name", + "recordingMethod": "manual" + }; + final hdp = HealthDataPoint.fromJson(json); + + expect(hdp.uuid, "some-uuid-2"); + expect(hdp.type, HealthDataType.AUDIOGRAM); + expect(hdp.unit, HealthDataUnit.DECIBEL_HEARING_LEVEL); + expect(hdp.sourcePlatform, HealthPlatformType.appleHealth); + expect(hdp.sourceDeviceId, "some-device-id"); + expect(hdp.sourceId, "some-source-id"); + expect(hdp.sourceName, "some-source-name"); + expect(hdp.recordingMethod, RecordingMethod.manual); + expect(hdp.value, isA()); + + final audiogramValue = hdp.value as AudiogramHealthValue; + expect(audiogramValue.frequencies, [1000.0, 2000.0, 3000.0]); + expect(audiogramValue.leftEarSensitivities, [20.0, 25.0, 30.0]); + expect(audiogramValue.rightEarSensitivities, [15.0, 20.0, 25.0]); + // debugPrint(toJsonString(hdp)); + expect(toJsonString(hdp), isA()); + }); + test('Test ElectrocardiogramHealthValue', () { + final json = { + "uuid": "some-uuid-3", + "value": { + "__type": "ElectrocardiogramHealthValue", + "voltageValues": [ + { + "__type": "ElectrocardiogramVoltageValue", + "voltage": 0.1, + "timeSinceSampleStart": 0.01 + }, + { + "__type": "ElectrocardiogramVoltageValue", + "voltage": 0.2, + "timeSinceSampleStart": 0.02 + }, + { + "__type": "ElectrocardiogramVoltageValue", + "voltage": 0.3, + "timeSinceSampleStart": 0.03 + } + ], + }, + "type": "ELECTROCARDIOGRAM", + "unit": "VOLT", + "dateFrom": "2024-09-24T17:34:00.000", + "dateTo": "2024-09-24T17:57:00.000", + "sourcePlatform": "appleHealth", + "sourceDeviceId": "some-device-id", + "sourceId": "some-source-id", + "sourceName": "some-source-name", + "recordingMethod": "active" + }; + + final hdp = HealthDataPoint.fromJson(json); + + expect(hdp.uuid, "some-uuid-3"); + expect(hdp.type, HealthDataType.ELECTROCARDIOGRAM); + expect(hdp.unit, HealthDataUnit.VOLT); + expect(hdp.sourcePlatform, HealthPlatformType.appleHealth); + expect(hdp.sourceDeviceId, "some-device-id"); + expect(hdp.sourceId, "some-source-id"); + expect(hdp.sourceName, "some-source-name"); + expect(hdp.recordingMethod, RecordingMethod.active); + expect(hdp.value, isA()); + + final ecgValue = hdp.value as ElectrocardiogramHealthValue; + expect(ecgValue.voltageValues.length, 3); + expect(ecgValue.voltageValues[0], isA()); + expect(ecgValue.voltageValues[0].voltage, 0.1); + expect(ecgValue.voltageValues[0].timeSinceSampleStart, 0.01); + expect(ecgValue.voltageValues[1].voltage, 0.2); + expect(ecgValue.voltageValues[1].timeSinceSampleStart, 0.02); + expect(ecgValue.voltageValues[2].voltage, 0.3); + expect(ecgValue.voltageValues[2].timeSinceSampleStart, 0.03); + // debugPrint(toJsonString(hdp)); + expect(toJsonString(hdp), isA()); + }); + test('Test NutritionHealthValue', () { + final json = { + "uuid": "some-uuid-4", + "value": { + "__type": "NutritionHealthValue", + "calories": 500.0, + "carbs": 60.0, + "protein": 20.0, + "fat": 30.0, + "caffeine": 100.0, + "vitaminA": 20.0, + "b1Thiamine": 20.0, + "b2Riboflavin": 20.0, + "b3Niacin": 20.0, + "b5PantothenicAcid": 20.0, + "b6Pyridoxine": 20.0, + "b7Biotin": 20.0, + "b9Folate": 20.0, + "b12Cobalamin": 20.0, + "vitaminC": 20.0, + "vitaminD": 20.0, + "vitaminE": 20.0, + "vitaminK": 20.0, + "calcium": 20.0, + "cholesterol": 20.0, + "chloride": 20.0, + "chromium": 20.0, + "copper": 20.0, + "fatUnsaturated": 20.0, + "fatMonounsaturated": 20.0, + "fatPolyunsaturated": 20.0, + "fatSaturated": 20.0, + "fatTransMonoenoic": 20.0, + "fiber": 20.0, + "iodine": 20.0, + "iron": 20.0, + "magnesium": 20.0, + "manganese": 20.0, + "molybdenum": 20.0, + "phosphorus": 20.0, + "potassium": 20.0, + "selenium": 20.0, + "sodium": 20.0, + "sugar": 20.0, + "water": 20.0, + "zinc": 20.0 + }, + "type": "NUTRITION", + "unit": "NO_UNIT", + "dateFrom": "2024-09-24T17:34:00.000", + "dateTo": "2024-09-24T17:57:00.000", + "sourcePlatform": "googleHealthConnect", + "sourceDeviceId": "some-device-id", + "sourceId": "some-source-id", + "sourceName": "some-source-name", + "recordingMethod": "manual" + }; + + final hdp = HealthDataPoint.fromJson(json); + expect(hdp.uuid, "some-uuid-4"); + expect(hdp.type, HealthDataType.NUTRITION); + expect(hdp.unit, HealthDataUnit.NO_UNIT); + expect(hdp.sourcePlatform, HealthPlatformType.googleHealthConnect); + expect(hdp.sourceDeviceId, "some-device-id"); + expect(hdp.sourceId, "some-source-id"); + expect(hdp.sourceName, "some-source-name"); + expect(hdp.recordingMethod, RecordingMethod.manual); + expect(hdp.value, isA()); + + final nutritionValue = hdp.value as NutritionHealthValue; + expect(nutritionValue.calories, 500.0); + expect(nutritionValue.carbs, 60.0); + expect(nutritionValue.protein, 20.0); + expect(nutritionValue.fat, 30.0); + expect(nutritionValue.caffeine, 100.0); + expect(nutritionValue.vitaminA, 20.0); + expect(nutritionValue.b1Thiamine, 20.0); + expect(nutritionValue.b2Riboflavin, 20.0); + expect(nutritionValue.b3Niacin, 20.0); + expect(nutritionValue.b5PantothenicAcid, 20.0); + expect(nutritionValue.b6Pyridoxine, 20.0); + expect(nutritionValue.b7Biotin, 20.0); + expect(nutritionValue.b9Folate, 20.0); + expect(nutritionValue.b12Cobalamin, 20.0); + expect(nutritionValue.vitaminC, 20.0); + expect(nutritionValue.vitaminD, 20.0); + expect(nutritionValue.vitaminE, 20.0); + expect(nutritionValue.vitaminK, 20.0); + expect(nutritionValue.calcium, 20.0); + expect(nutritionValue.cholesterol, 20.0); + expect(nutritionValue.chloride, 20.0); + expect(nutritionValue.chromium, 20.0); + expect(nutritionValue.copper, 20.0); + expect(nutritionValue.fatUnsaturated, 20.0); + expect(nutritionValue.fatMonounsaturated, 20.0); + expect(nutritionValue.fatPolyunsaturated, 20.0); + expect(nutritionValue.fatSaturated, 20.0); + expect(nutritionValue.fatTransMonoenoic, 20.0); + expect(nutritionValue.fiber, 20.0); + expect(nutritionValue.iodine, 20.0); + expect(nutritionValue.iron, 20.0); + expect(nutritionValue.magnesium, 20.0); + expect(nutritionValue.manganese, 20.0); + expect(nutritionValue.molybdenum, 20.0); + expect(nutritionValue.phosphorus, 20.0); + expect(nutritionValue.potassium, 20.0); + expect(nutritionValue.selenium, 20.0); + expect(nutritionValue.sodium, 20.0); + expect(nutritionValue.sugar, 20.0); + expect(nutritionValue.water, 20.0); + expect(nutritionValue.zinc, 20.0); + // debugPrint(toJsonString(hdp)); + expect(toJsonString(hdp), isA()); + }); + test('Test HealthValue error handling', () { + final json = { + "uuid": "some-uuid-error", + "value": { + "__type": "UnknownHealthValue", // This should throw an error + "numericValue": 123.45 + }, + "type": "HEART_RATE", + "unit": "COUNT_PER_MINUTE", + "dateFrom": "2024-09-24T17:34:00.000", + "dateTo": "2024-09-24T17:57:00.000", + "sourcePlatform": "googleHealthConnect", + "sourceDeviceId": "some-device-id", + "sourceId": "some-source-id", + "sourceName": "some-source-name", + "recordingMethod": "automatic" + }; + expect( + () => HealthDataPoint.fromJson(json), + throwsA( + isA())); //Expect SerializationException + }); + }); +} diff --git a/packages/health/test/mocks/device_info_mock.dart b/packages/health/test/mocks/device_info_mock.dart new file mode 100644 index 000000000..019a36012 --- /dev/null +++ b/packages/health/test/mocks/device_info_mock.dart @@ -0,0 +1,60 @@ +import 'package:mocktail/mocktail.dart'; +import 'package:device_info_plus/device_info_plus.dart'; + +class MockDeviceInfoPlugin extends Mock implements DeviceInfoPlugin { + @override + Future get androidInfo => + Future.value(AndroidDeviceInfo.fromMap({ + 'id': 'mock-android-id', + 'version': { + 'baseOS': 'mock-baseOS', + 'codename': 'mock-codename', + 'incremental': 'mock-incremental', + 'previewSdkInt': 23, + 'release': 'mock-release', + 'sdkInt': 30, + 'securityPatch': 'mock-securityPatch', + }, + 'board': 'mock-board', + 'bootloader': 'mock-bootloader', + 'brand': 'mock-brand', + 'device': 'mock-device', + 'display': 'mock-display', + 'fingerprint': 'mock-fingerprint', + 'hardware': 'mock-hardware', + 'host': 'mock-host', + 'manufacturer': 'mock-manufacturer', + 'model': 'mock-model', + 'product': 'mock-product', + 'supported32BitAbis': [], + 'supported64BitAbis': [], + 'supportedAbis': [], + 'tags': 'mock-tags', + 'type': 'mock-type', + 'isPhysicalDevice': true, + 'systemFeatures': [], + 'serialNumber': 'mock-serial', + 'isLowRamDevice': false, + })); + + + @override + Future get iosInfo => Future.value(IosDeviceInfo.fromMap({ + 'name': 'mock-ios-name', + 'systemName': 'mock-ios-systemName', + 'systemVersion': '16.0', + 'model': 'mock-ios-model', + 'modelName': 'mock-ios-modelName', + 'localizedModel': 'mock-ios-localizedModel', + 'identifierForVendor': 'mock-ios-id', + 'isPhysicalDevice': true, + 'isiOSAppOnMac': false, + 'utsname': { + 'sysname': 'mock-ios-sysname', + 'nodename': 'mock-ios-nodename', + 'release': 'mock-ios-release', + 'version': 'mock-ios-version', + 'machine': 'mock-ios-machine', + }, + })); +} \ No newline at end of file From 8b34d36d00de1a4fd2f97488af70be3716ad8126 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:24:28 +0100 Subject: [PATCH 72/80] test: clean up health_test.dart --- packages/health/test/health_test.dart | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/health/test/health_test.dart b/packages/health/test/health_test.dart index 9e6759973..dc7c88cbc 100644 --- a/packages/health/test/health_test.dart +++ b/packages/health/test/health_test.dart @@ -1,19 +1,13 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:health/health.dart'; -import 'dart:convert'; import 'package:carp_serializable/carp_serializable.dart'; -import 'mocks/device_info_mock.dart'; // Import the mock file +import 'mocks/device_info_mock.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('HealthDataPoint fromJson Tests', () { - // Helper function to print the toJsonString, - // useful for debugging failed tests - String toJsonString(HealthDataPoint hdp) { - return jsonEncode(hdp.toJson()); - } //Instantiate Health class with the Mock final health = Health(deviceInfo: MockDeviceInfoPlugin()); @@ -70,7 +64,6 @@ void main() { HealthDataUnit.METER); // debugPrint(toJsonString(hdp)); - // don't print try to see if toJsonString works expect(toJsonString(hdp), isA()); @@ -103,6 +96,7 @@ void main() { expect(hdp.value, isA()); expect((hdp.value as NumericHealthValue).numericValue, 123.45); + // debugPrint(toJsonString(hdp)); expect(toJsonString(hdp), isA()); }); @@ -141,6 +135,7 @@ void main() { expect(audiogramValue.frequencies, [1000.0, 2000.0, 3000.0]); expect(audiogramValue.leftEarSensitivities, [20.0, 25.0, 30.0]); expect(audiogramValue.rightEarSensitivities, [15.0, 20.0, 25.0]); + // debugPrint(toJsonString(hdp)); expect(toJsonString(hdp), isA()); }); From 87745f3366f10dec1faf2653a6fec12229b40197 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:35:13 +0100 Subject: [PATCH 73/80] chore: update CHANGELOG.md to include fix for issue #1051 --- packages/health/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index e6efc7c4e..5b1f02d8f 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -16,6 +16,7 @@ * 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 From 8f7884583afd3035b76770a44f3a8b320658a4a4 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:45:20 +0100 Subject: [PATCH 74/80] chore: update CHANGELOG.md --- packages/health/CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index a838ae0b2..40f8b6f04 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -9,7 +9,6 @@ - Previously, the `Health` class directly instantiated the `DeviceInfoPlugin` internally, which was not ideal when trying to mock this functionality in unit tests, and could lead to problems with state management. * **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) From 90d950232b98e56d16eef884890d7ffcd326b145 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:46:56 +0100 Subject: [PATCH 75/80] chore: update CHANGELOG.md --- packages/health/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 40f8b6f04..7c6d28359 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -5,7 +5,7 @@ - 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, as explained in the **Initialization** section below. + - This architectural change means that, for the application to work correctly, the `Health` class *MUST* be initialized correctly as a global instance. - Previously, the `Health` class directly instantiated the `DeviceInfoPlugin` internally, which was not ideal when trying to mock this functionality in unit tests, and could lead to problems with state management. * **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();`). From 028b74b0e1f4d3d52755b079f94d76abd0fc41c3 Mon Sep 17 00:00:00 2001 From: Alireza Hajebrahimi <6937697+iarata@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:49:06 +0100 Subject: [PATCH 76/80] chore: update CHANGELOG.md --- packages/health/CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 7c6d28359..7753e1d0e 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -6,7 +6,6 @@ - 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. - - Previously, the `Health` class directly instantiated the `DeviceInfoPlugin` internally, which was not ideal when trying to mock this functionality in unit tests, and could lead to problems with state management. * **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. From ff955302768b462f642101dc2e3b3213650e7413 Mon Sep 17 00:00:00 2001 From: bardram Date: Wed, 15 Jan 2025 17:35:08 +0100 Subject: [PATCH 77/80] Update LICENSE --- packages/mobility_features/LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobility_features/LICENSE b/packages/mobility_features/LICENSE index c0eab2edb..e584a9db2 100644 --- a/packages/mobility_features/LICENSE +++ b/packages/mobility_features/LICENSE @@ -1,6 +1,6 @@ MIT License. -Copyright 2018-2021 Copenhagen Center for Health Technology (CACHET) at the Technical University of Denmark (DTU). +Copyright 2018-2024 the Technical University of Denmark (DTU). Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ”Software”), to deal in the Software without restriction, including without limitation From 7c332751527677cdafe30f4552128565125a3693 Mon Sep 17 00:00:00 2001 From: bardram Date: Wed, 15 Jan 2025 18:07:20 +0100 Subject: [PATCH 78/80] Small updates to documentation and improving on pub.dev scores --- packages/health/example/lib/main.dart | 42 +++++++++---------- packages/health/lib/health.dart | 2 +- packages/health/lib/src/health_plugin.dart | 22 ++++++---- .../health/lib/src/health_value_types.dart | 2 - 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart index f67f860a0..59c963dac 100644 --- a/packages/health/example/lib/main.dart +++ b/packages/health/example/lib/main.dart @@ -16,7 +16,7 @@ class HealthApp extends StatefulWidget { const HealthApp({super.key}); @override - _HealthAppState createState() => _HealthAppState(); + HealthAppState createState() => HealthAppState(); } enum AppState { @@ -37,7 +37,7 @@ enum AppState { PERMISSIONS_NOT_REVOKED, } -class _HealthAppState extends State { +class HealthAppState extends State { List _healthDataList = []; AppState _state = AppState.DATA_NOT_FETCHED; int _nofSteps = 0; @@ -288,12 +288,12 @@ class _HealthAppState extends State { type: HealthDataType.SLEEP_DEEP, startTime: earlier, endTime: now); - success &= await health.writeHealthData( - value: 22, - type: HealthDataType.LEAN_BODY_MASS, + success &= await health.writeHealthData( + value: 22, + type: HealthDataType.LEAN_BODY_MASS, startTime: earlier, endTime: now, - ); + ); // specialized write methods success &= await health.writeBloodOxygen( @@ -386,22 +386,20 @@ class _HealthAppState extends State { ); // Available on iOS 16.0+ only - if (Platform.isIOS) { + if (Platform.isIOS) { success &= await health.writeHealthData( - value: 22, - type: HealthDataType.WATER_TEMPERATURE, - startTime: earlier, - endTime: now, - recordingMethod: RecordingMethod.manual - ); + value: 22, + type: HealthDataType.WATER_TEMPERATURE, + startTime: earlier, + endTime: now, + recordingMethod: RecordingMethod.manual); - success &= await health.writeHealthData( - value: 55, - type: HealthDataType.UNDERWATER_DEPTH, - startTime: earlier, - endTime: now, - recordingMethod: RecordingMethod.manual - ); + success &= await health.writeHealthData( + value: 55, + type: HealthDataType.UNDERWATER_DEPTH, + startTime: earlier, + endTime: now, + recordingMethod: RecordingMethod.manual); } setState(() { @@ -702,8 +700,8 @@ class _HealthAppState extends State { return ListTile( title: Text( "${p.typeString}: ${(p.value as WorkoutHealthValue).totalEnergyBurned} ${(p.value as WorkoutHealthValue).totalEnergyBurnedUnit?.name}"), - trailing: Text( - (p.value as WorkoutHealthValue).workoutActivityType.name), + trailing: + Text((p.value as WorkoutHealthValue).workoutActivityType.name), subtitle: Text('${p.dateFrom} - ${p.dateTo}\n${p.recordingMethod}'), ); } diff --git a/packages/health/lib/health.dart b/packages/health/lib/health.dart index 1c960d54f..af94d80e7 100644 --- a/packages/health/lib/health.dart +++ b/packages/health/lib/health.dart @@ -1,4 +1,4 @@ -library health; +library; import 'dart:async'; import 'dart:collection'; diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart index 06b76bb52..cc62e3ac7 100644 --- a/packages/health/lib/src/health_plugin.dart +++ b/packages/health/lib/src/health_plugin.dart @@ -1,8 +1,12 @@ part of '../health.dart'; -/// Main class for the Plugin. This class works as a singleton and should be -/// accessed via `Health()` factory method. The plugin must be configured using -/// the [configure] method before used. +/// Main class for the Plugin. +/// +/// Use this class to get an instance of the Health plugin, like this: +/// +/// final health = Health(); +/// +/// The plugin must be configured using the [configure] method before used. /// /// Overall, the plugin supports: /// @@ -37,12 +41,14 @@ class Health { String? _deviceId; final DeviceInfoPlugin _deviceInfo; - HealthConnectSdkStatus _healthConnectSdkStatus = - HealthConnectSdkStatus.sdkUnavailable; + HealthConnectSdkStatus _healthConnectSdkStatus = + HealthConnectSdkStatus.sdkUnavailable; - Health({DeviceInfoPlugin? deviceInfo}) : _deviceInfo = deviceInfo ?? DeviceInfoPlugin() { - _registerFromJsonFunctions(); - } + /// Get an instance of the health plugin. + Health({DeviceInfoPlugin? deviceInfo}) + : _deviceInfo = deviceInfo ?? DeviceInfoPlugin() { + _registerFromJsonFunctions(); + } /// The latest status on availability of Health Connect SDK on this phone. HealthConnectSdkStatus get healthConnectSdkStatus => _healthConnectSdkStatus; diff --git a/packages/health/lib/src/health_value_types.dart b/packages/health/lib/src/health_value_types.dart index 4b88adc50..af13e1892 100644 --- a/packages/health/lib/src/health_value_types.dart +++ b/packages/health/lib/src/health_value_types.dart @@ -608,8 +608,6 @@ class NutritionHealthValue extends HealthValue { @override Map toJson() => _$NutritionHealthValueToJson(this); - static double? _toDoubleOrNull(num? value) => value?.toDouble(); - /// Create a [NutritionHealthValue] based on a health data point from native data format. factory NutritionHealthValue.fromHealthDataPoint(dynamic dataPoint) { dataPoint = dataPoint as Map; From 5d8b1b36a11e21e50a4327a60ec75d4b447142b6 Mon Sep 17 00:00:00 2001 From: bardram Date: Wed, 15 Jan 2025 18:11:54 +0100 Subject: [PATCH 79/80] pubspect to 12.0.0 --- packages/health/CHANGELOG.md | 14 ++++++++------ packages/health/pubspec.yaml | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index 7753e1d0e..d988ea7d1 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,22 +1,24 @@ ## 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. + * **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();`). + * 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 ``` + * 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) diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index 16c5823af..3fb67cdc1 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -1,6 +1,6 @@ name: health description: Wrapper for Apple's HealthKit on iOS and Google's Health Connect on Android. -version: 11.1.1 +version: 12.0.0 homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages/health environment: From 65f9368ea2093dad7f505e483cde2328c24c229e Mon Sep 17 00:00:00 2001 From: bardram Date: Thu, 16 Jan 2025 18:41:15 +0100 Subject: [PATCH 80/80] fix of #1118 and update to README doc --- packages/health/CHANGELOG.md | 5 +++++ packages/health/README.md | 26 +++++++++++++++----------- packages/health/lib/health.json.dart | 5 +++++ packages/health/pubspec.yaml | 2 +- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md index d988ea7d1..704d1cd24 100644 --- a/packages/health/CHANGELOG.md +++ b/packages/health/CHANGELOG.md @@ -1,3 +1,8 @@ +## 12.0.1 + +* Update of API and README doc +* Fix [#1118](https://github.com/cph-cachet/flutter-plugins/issues/1118) + ## 12.0.0 * **BREAKING** This release introduces a significant architectural change to the `health` plugin by removing the `singleton` pattern. diff --git a/packages/health/README.md b/packages/health/README.md index eea60737c..ffa41fc89 100644 --- a/packages/health/README.md +++ b/packages/health/README.md @@ -146,12 +146,16 @@ android.useAndroidX=true See the example app for detailed examples of how to use the Health API. -The Health plugin is used via the `Health()` singleton using the different methods for handling permissions and getting and adding data to Apple Health or Google Health Connect. +A instance of the Health plugin is create using the `Health()` constructor and is subsequently configured calling the `configure` method. Once configured, the plugin can be used for handling permissions and getting and adding data to Apple Health or Google Health Connect. Below is a simplified flow of how to use the plugin. ```dart + + // Global Health instance + final health = Health(); + // configure the health plugin before use. - Health().configure(); + await health.configure(); // define the types to get @@ -161,12 +165,12 @@ Below is a simplified flow of how to use the plugin. ]; // requesting access to the data types before reading them - bool requested = await Health().requestAuthorization(types); + bool requested = await health.requestAuthorization(types); var now = DateTime.now(); // fetch health data from the last 24 hours - List healthData = await Health().getHealthDataFromTypes( + List healthData = await health.getHealthDataFromTypes( now.subtract(Duration(days: 1)), now, types); // request permissions to write steps and blood glucose @@ -175,20 +179,20 @@ Below is a simplified flow of how to use the plugin. HealthDataAccess.READ_WRITE, HealthDataAccess.READ_WRITE ]; - await Health().requestAuthorization(types, permissions: permissions); + await health.requestAuthorization(types, permissions: permissions); // write steps and blood glucose - bool success = await Health().writeHealthData(10, HealthDataType.STEPS, now, now); - success = await Health().writeHealthData(3.1, HealthDataType.BLOOD_GLUCOSE, now, now); + bool success = await health.writeHealthData(10, HealthDataType.STEPS, now, now); + success = await health.writeHealthData(3.1, HealthDataType.BLOOD_GLUCOSE, now, now); // you can also specify the recording method to store in the metadata (default is RecordingMethod.automatic) // on iOS only `RecordingMethod.automatic` and `RecordingMethod.manual` are supported // Android additionally supports `RecordingMethod.active` and `RecordingMethod.unknown` - success &= await Health().writeHealthData(10, HealthDataType.STEPS, now, now, recordingMethod: RecordingMethod.manual); + success &= await health.writeHealthData(10, HealthDataType.STEPS, now, now, recordingMethod: RecordingMethod.manual); // get the number of steps for today var midnight = DateTime(now.year, now.month, now.day); - int? steps = await Health().getTotalStepsInInterval(midnight, now); + int? steps = await health.getTotalStepsInInterval(midnight, now); ``` ### Health Data @@ -266,7 +270,7 @@ Google Health Connect and Apple HealthKit both provide ways to distinguish sampl As such, when fetching data you have the option to filter the fetched data by recording method as such: ```dart -List healthData = await Health().getHealthDataFromTypes( +List healthData = await health.getHealthDataFromTypes( types: types, startTime: yesterday, endTime: now, @@ -294,7 +298,7 @@ If you have a list of data points, duplicates can be removed with: ```dart List points = ...; -points = Health().removeDuplicates(points); +points = health.removeDuplicates(points); ``` ## Data Types diff --git a/packages/health/lib/health.json.dart b/packages/health/lib/health.json.dart index 2162869da..4694720ea 100644 --- a/packages/health/lib/health.json.dart +++ b/packages/health/lib/health.json.dart @@ -19,6 +19,11 @@ void _registerFromJsonFunctions() { ElectrocardiogramHealthValue(voltageValues: []), ElectrocardiogramVoltageValue(voltage: 12, timeSinceSampleStart: 0), NutritionHealthValue(), + MenstruationFlowHealthValue(flow: null, dateTime: DateTime.now()), + InsulinDeliveryHealthValue( + units: 0.0, + reason: InsulinDeliveryReason.NOT_SET, + ), ]); _fromJsonFunctionsRegistered = true; diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml index 3fb67cdc1..ed206e548 100644 --- a/packages/health/pubspec.yaml +++ b/packages/health/pubspec.yaml @@ -1,6 +1,6 @@ name: health description: Wrapper for Apple's HealthKit on iOS and Google's Health Connect on Android. -version: 12.0.0 +version: 12.0.1 homepage: https://github.com/cph-cachet/flutter-plugins/tree/master/packages/health environment: