public override async Task <bool> WriteAsync(HealthDataType healthDataType, double value, DateTime start, DateTime?end = null) { if (end == null) { end = start; } var healthKit = healthDataType.ToHealthKit(); if (healthKit.HKType == HKTypes.Category) { var type = HKCategoryType.Create(healthDataType.ToHealthKit().CategoryTypeIdentifier); var sample = HKCategorySample.FromType(type, (nint)value, (NSDate)start, (NSDate)end); var(success, error) = await _healthStore.SaveObjectAsync(sample).ConfigureAwait(false); return(success); } else if (healthKit.HKType == HKTypes.Quantity) { var type = HKQuantityType.Create(healthDataType.ToHealthKit().QuantityTypeIdentifier); var quantity = HKQuantity.FromQuantity(healthDataType.ToHealthKit().Unit, value); var sample = HKQuantitySample.FromType(type, quantity, (NSDate)start, (NSDate)end); var(success, error) = await _healthStore.SaveObjectAsync(sample).ConfigureAwait(false); return(success); } else { throw new NotSupportedException(); } }
public override bool IsDataTypeAvailable(HealthDataType healthDataType) { try { return(healthDataType.ToHealthKit() != null); } catch (ArgumentOutOfRangeException e) { Debug.WriteLine($"HEALTHKIT - Datatype {healthDataType} is not supported in this device", e); return(false); } }
bool IsAuthorizedToRead(HealthDataType healthDataType) { if (_healthService.IsDataTypeAvailable(healthDataType)) { //Note: authorizationStatus is to determine the access status only to write but not to read. //There is no option to know whether your app has read access. //https://stackoverflow.com/a/29128231/9823528 //So for now i just return true if the OS supports the datatype return(true); } return(false); }
public abstract bool IsDataTypeAvailable(HealthDataType healthDataType);
protected override async Task <IEnumerable <T> > Query <T>(HealthDataType healthDataType, AggregateTime aggregateTime, DateTime startDate, DateTime endDate) { var authorized = _healthService.HasOAuthPermission(_healthService.FitnessReadOptions(new HealthDataType[] { healthDataType })); if (!authorized) { throw new UnauthorizedAccessException($"Not enough permissions to request {healthDataType}"); } var fitData = healthDataType.ToGoogleFit(); long startTime = startDate.ToJavaTimeStamp(); long endTime = endDate.ToJavaTimeStamp(); var readBuilder = new DataReadRequest.Builder() .SetTimeRange(startTime, endTime, TimeUnit.Milliseconds) .EnableServerQueries(); if (aggregateTime != AggregateTime.None) { readBuilder.Aggregate(fitData.TypeIdentifier, fitData.AggregateType); switch (aggregateTime) { case AggregateTime.Year: readBuilder.BucketByTime(365, TimeUnit.Days); break; case AggregateTime.Month: readBuilder.BucketByTime(31, TimeUnit.Days); break; case AggregateTime.Week: readBuilder.BucketByTime(7, TimeUnit.Days); break; case AggregateTime.Day: readBuilder.BucketByTime(1, TimeUnit.Days); break; case AggregateTime.Hour: readBuilder.BucketByTime(1, TimeUnit.Hours); break; } } else { readBuilder.Read(fitData.TypeIdentifier); } var readRequest = readBuilder.Build(); var response = await FitnessClass.GetHistoryClient(_currentActivity, GoogleSignIn.GetLastSignedInAccount(_currentActivity)) .ReadDataAsync(readRequest).ConfigureAwait(false); double valueToSubstract = 0; if (healthDataType == HealthDataType.CaloriesActive) { valueToSubstract = await GetBasalAvg(endDate); } if (response == null) { return(new List <T>()); } if (response.Buckets.Any()) { var output = new List <T>(); foreach (var bucket in response.Buckets) { var dataSet = bucket.GetDataSet(fitData.AggregateType); output.AddRange((IEnumerable <T>)dataSet.DataPoints.Select(result => CreateAggregatedData(result, fitData, valueToSubstract))); if (!dataSet.DataPoints.Any() && healthDataType == HealthDataType.Droid_BasalMetabolicRate) { output.Add( new AggregatedHealthData() { StartDate = bucket.GetStartTime(TimeUnit.Milliseconds).ToDateTime(), EndDate = bucket.GetEndTime(TimeUnit.Milliseconds).ToDateTime(), Sum = valueToSubstract } as T); } } return(output); } return((IEnumerable <T>)response.GetDataSet(fitData.TypeIdentifier)?.DataPoints? .Select(dataPoint => new HealthData { StartDate = dataPoint.GetStartTime(TimeUnit.Milliseconds).ToDateTime(), EndDate = dataPoint.GetEndTime(TimeUnit.Milliseconds).ToDateTime(), Value = ReadValue(dataPoint, fitData.Unit, valueToSubstract) ?? 0, UserEntered = dataPoint.OriginalDataSource?.StreamName == "user_input" }).ToList()); }
protected override Task <IEnumerable <T> > Query <T>(HealthDataType healthDataType, AggregateTime aggregateTime, DateTime startDate, DateTime endDate) { if (_healthStore == null || !HKHealthStore.IsHealthDataAvailable) { throw new NotSupportedException("HealthKit data is not available on this device"); } var authorized = IsAuthorizedToRead(healthDataType); if (!authorized) { throw new UnauthorizedAccessException($"Not enough permissions to request {healthDataType}"); } var taskComplSrc = new TaskCompletionSource <IEnumerable <T> >(); var healthKitType = healthDataType.ToHealthKit(); var quantityType = HKQuantityType.Create(healthKitType.QuantityTypeIdentifier); var predicate = HKQuery.GetPredicateForSamples((NSDate)startDate, (NSDate)endDate, HKQueryOptions.StrictStartDate); if (aggregateTime != AggregateTime.None) { var anchor = NSCalendar.CurrentCalendar.DateBySettingsHour(0, 0, 0, NSDate.Now, NSCalendarOptions.None); var interval = new NSDateComponents(); switch (aggregateTime) { case AggregateTime.Year: interval.Year = 1; break; case AggregateTime.Month: interval.Month = 1; break; case AggregateTime.Week: interval.Week = 1; break; case AggregateTime.Day: interval.Day = 1; break; case AggregateTime.Hour: interval.Hour = 1; break; } HKStatisticsOptions hkStatisticsOptions; if (healthKitType.Cumulative) { hkStatisticsOptions = HKStatisticsOptions.CumulativeSum; } else { hkStatisticsOptions = HKStatisticsOptions.DiscreteAverage | HKStatisticsOptions.DiscreteMax | HKStatisticsOptions.DiscreteMax; } var queryAggregate = new HKStatisticsCollectionQuery(quantityType, predicate, hkStatisticsOptions, anchor, interval) { InitialResultsHandler = (collectionQuery, results, error) => { var healthData = new List <T>(); foreach (var result in results.Statistics) { var hData = new AggregatedHealthData { StartDate = (DateTime)result.StartDate, EndDate = (DateTime)result.EndDate, }; if (healthKitType.Cumulative) { hData.Sum = result.SumQuantity().GetDoubleValue(healthKitType.Unit); } else { hData.Min = result.MinimumQuantity().GetDoubleValue(healthKitType.Unit); hData.Max = result.MaximumQuantity().GetDoubleValue(healthKitType.Unit); hData.Average = result.AverageQuantity().GetDoubleValue(healthKitType.Unit); } healthData.Add(hData as T); } taskComplSrc.SetResult(healthData); } }; _healthStore.ExecuteQuery(queryAggregate); } else { var sortDescriptor = new[] { new NSSortDescriptor(HKSample.SortIdentifierEndDate, true) }; HKSampleType sampleType; if (healthKitType.HKType == HealthKitData.HKTypes.Category) { sampleType = HKCategoryType.Create(healthKitType.CategoryTypeIdentifier); } else if (healthKitType.HKType == HealthKitData.HKTypes.Quantity) { sampleType = HKQuantityType.Create(healthKitType.QuantityTypeIdentifier); } else if (healthKitType.HKType == HealthKitData.HKTypes.Workout) { sampleType = HKSampleType.GetWorkoutType(); } else { throw new NotSupportedException(); } var query = new HKSampleQuery(sampleType, predicate, HKSampleQuery.NoLimit, sortDescriptor, (resultQuery, results, error) => { IEnumerable <T> healthData = default(IEnumerable <T>); if (sampleType == HKSampleType.GetWorkoutType()) { healthData = results?.Select(result => new WorkoutData { StartDate = (DateTime)result.StartDate, EndDate = (DateTime)result.EndDate, Duration = (result as HKWorkout).Duration, Device = (result as HKWorkout).Device?.ToString(), WorkoutType = (result as HKWorkout).WorkoutDataType() //TotalDistance = Convert.ToDouble((result as HKWorkout).TotalDistance), //TotalEnergyBurned = Convert.ToDouble((result as HKWorkout).TotalEnergyBurned) } as T); } else { healthData = results?.Select(result => new HealthData { StartDate = (DateTime)result.StartDate, EndDate = (DateTime)result.EndDate, Value = ReadValue(result, healthKitType.Unit), UserEntered = result.Metadata?.WasUserEntered ?? false, } as T); } taskComplSrc.SetResult(healthData); }); _healthStore.ExecuteQuery(query); } return(taskComplSrc.Task); }
protected abstract Task <IEnumerable <T> > Query <T>(HealthDataType dataTypes, AggregateTime aggregateTime, DateTime startDate, DateTime endDate) where T : class, IHealthData;
public abstract Task <bool> WriteAsync(HealthDataType healthDataType, double value, DateTime start, DateTime?end);
//More info for init HKUnit: //https://developer.apple.com/documentation/healthkit/hkunit/1615733-init //More info on datatype: //https://github.com/dariosalvi78/cordova-plugin-health //TODO: come gestire active+basal per calorie? //TODO: HKQuantityTypeIdentifierActiveEnergyBurned + HKQuantityTypeIdentifierBasalEnergyBurned internal static HealthKitData ToHealthKit(this HealthDataType healthDataType) { switch (healthDataType) { case HealthDataType.StepCount: return(new HealthKitData { Unit = HKUnit.Count, TypeIdentifier = HKQuantityTypeIdentifier.StepCount, Cumulative = true }); case HealthDataType.Distance: return(new HealthKitData { Unit = HKUnit.Meter, TypeIdentifier = HKQuantityTypeIdentifier.DistanceWalkingRunning, Cumulative = true }); case HealthDataType.HeartRate: return(new HealthKitData { Unit = HKUnit.Count.UnitDividedBy(HKUnit.Minute), TypeIdentifier = HKQuantityTypeIdentifier.HeartRate }); case HealthDataType.Height: return(new HealthKitData { Unit = HKUnit.Meter, TypeIdentifier = HKQuantityTypeIdentifier.Height }); case HealthDataType.Weight: return(new HealthKitData { Unit = HKUnit.Gram, TypeIdentifier = HKQuantityTypeIdentifier.BodyMass }); case HealthDataType.Calories: return(new HealthKitData { Unit = HKUnit.Kilocalorie, TypeIdentifier = HKQuantityTypeIdentifier.ActiveEnergyBurned, Cumulative = true }); case HealthDataType.CaloriesActive: return(new HealthKitData { Unit = HKUnit.Kilocalorie, TypeIdentifier = HKQuantityTypeIdentifier.ActiveEnergyBurned, Cumulative = true }); /*case HealthDataType.CaloriesBasal: * return new HealthKitData * { * Unit = HKUnit.Kilocalorie, * TypeIdentifier = HKQuantityTypeIdentifier.BasalEnergyBurned, * Cumulative = true * };*/ case HealthDataType.Water: return(new HealthKitData { Unit = HKUnit.Liter, TypeIdentifier = HKQuantityTypeIdentifier.DietaryWater, Cumulative = true }); case HealthDataType.BodyFat: return(new HealthKitData { Unit = HKUnit.Percent, TypeIdentifier = HKQuantityTypeIdentifier.BodyFatPercentage }); case HealthDataType.BodyTemperature: return(new HealthKitData { Unit = HKUnit.DegreeCelsius, TypeIdentifier = HKQuantityTypeIdentifier.BodyTemperature }); case HealthDataType.BloodPressureSystolic: return(new HealthKitData { Unit = HKUnit.MillimeterOfMercury, TypeIdentifier = HKQuantityTypeIdentifier.BloodPressureSystolic }); case HealthDataType.BloodPressureDiastolic: return(new HealthKitData { Unit = HKUnit.MillimeterOfMercury, TypeIdentifier = HKQuantityTypeIdentifier.BloodPressureDiastolic }); case HealthDataType.BloodOxygen: return(new HealthKitData { Unit = HKUnit.Percent, TypeIdentifier = HKQuantityTypeIdentifier.OxygenSaturation }); case HealthDataType.BloodGlucose: return(new HealthKitData { Unit = HKUnit.FromString("mg/dl"), TypeIdentifier = HKQuantityTypeIdentifier.BloodGlucose }); // IOS SPECIFIC case HealthDataType.iOS_BodyMassIndex: return(new HealthKitData { Unit = HKUnit.FromString(""), TypeIdentifier = HKQuantityTypeIdentifier.BodyMassIndex }); case HealthDataType.iOS_WalkingHeartRate: return(new HealthKitData { Unit = HKUnit.Count.UnitDividedBy(HKUnit.Minute), TypeIdentifier = HKQuantityTypeIdentifier.WalkingHeartRateAverage }); case HealthDataType.iOS_RestingHeartRate: return(new HealthKitData { Unit = HKUnit.Count.UnitDividedBy(HKUnit.Minute), TypeIdentifier = HKQuantityTypeIdentifier.RestingHeartRate }); case HealthDataType.iOS_BasalEnergyBurned: return(new HealthKitData { Unit = HKUnit.Kilocalorie, TypeIdentifier = HKQuantityTypeIdentifier.BasalEnergyBurned, Cumulative = true }); case HealthDataType.iOS_WaistCircumference: return(new HealthKitData { Unit = HKUnit.Meter, TypeIdentifier = HKQuantityTypeIdentifier.WaistCircumference }); case HealthDataType.iOS_StandTime: ThrowIfUnsupported(13, 0); return(new HealthKitData { Unit = HKUnit.Minute, TypeIdentifier = HKQuantityTypeIdentifier.AppleStandTime, Cumulative = true }); case HealthDataType.iOS_ExerciseTime: return(new HealthKitData { Unit = HKUnit.Minute, TypeIdentifier = HKQuantityTypeIdentifier.AppleExerciseTime, Cumulative = true }); default: throw new ArgumentOutOfRangeException(); } }
//TODO: come gestire active+basal per calorie? //TODO: TYPE_CALORIES_EXPENDED = (TYPE_CALORIES_EXPENDED - (TYPE_BASAL_METABOLIC_RATE * time window))+ (TYPE_BASAL_METABOLIC_RATE * time window) internal static GoogleFitData ToGoogleFit(this HealthDataType healthDataType) { switch (healthDataType) { case HealthDataType.StepCount: return(new GoogleFitData { TypeIdentifier = DataType.TypeStepCountDelta, AggregateType = DataType.AggregateStepCountDelta, Unit = Field.FieldSteps, Cumulative = true }); case HealthDataType.Distance: return(new GoogleFitData { TypeIdentifier = DataType.TypeDistanceDelta, AggregateType = DataType.AggregateDistanceDelta, Unit = Field.FieldDistance, Cumulative = true }); case HealthDataType.HeartRate: return(new GoogleFitData { TypeIdentifier = DataType.TypeHeartRateBpm, AggregateType = DataType.AggregateHeartRateSummary, Unit = Field.FieldBpm }); case HealthDataType.Height: return(new GoogleFitData { TypeIdentifier = DataType.TypeHeight, AggregateType = DataType.AggregateHeightSummary, Unit = Field.FieldHeight }); case HealthDataType.Weight: return(new GoogleFitData { TypeIdentifier = DataType.TypeWeight, AggregateType = DataType.AggregateWeightSummary, Unit = Field.FieldWeight }); case HealthDataType.Calories: return(new GoogleFitData { TypeIdentifier = DataType.TypeCaloriesExpended, AggregateType = DataType.AggregateCaloriesExpended, Unit = Field.FieldCalories, Cumulative = true }); case HealthDataType.CaloriesActive: return(new GoogleFitData { TypeIdentifier = DataType.TypeCaloriesExpended, AggregateType = DataType.AggregateCaloriesExpended, Unit = Field.FieldCalories, Cumulative = true }); case HealthDataType.Water: return(new GoogleFitData { TypeIdentifier = DataType.TypeHydration, AggregateType = DataType.AggregateHydration, Unit = Field.FieldVolume, Cumulative = true }); case HealthDataType.BodyFat: return(new GoogleFitData { TypeIdentifier = DataType.TypeBodyFatPercentage, AggregateType = DataType.TypeBodyFatPercentage, Unit = Field.FieldPercentage }); case HealthDataType.BodyTemperature: return(new GoogleFitData { TypeIdentifier = HealthDataTypes.TypeBodyTemperature, AggregateType = HealthDataTypes.AggregateBodyTemperatureSummary, Unit = HealthFields.FieldBodyTemperature }); case HealthDataType.BloodPressureSystolic: return(new GoogleFitData { TypeIdentifier = HealthDataTypes.TypeBloodPressure, AggregateType = HealthDataTypes.AggregateBloodPressureSummary, Unit = HealthFields.FieldBloodPressureSystolic, MinOverride = HealthFields.FieldBloodPressureSystolicMin, MaxOverride = HealthFields.FieldBloodPressureSystolicMax, AverageOverride = HealthFields.FieldBloodPressureSystolicAverage, }); case HealthDataType.BloodPressureDiastolic: return(new GoogleFitData { TypeIdentifier = HealthDataTypes.TypeBloodPressure, AggregateType = HealthDataTypes.AggregateBloodPressureSummary, Unit = HealthFields.FieldBloodPressureDiastolic, MinOverride = HealthFields.FieldBloodPressureDiastolicMin, MaxOverride = HealthFields.FieldBloodPressureDiastolicMax, AverageOverride = HealthFields.FieldBloodPressureDiastolicAverage, }); case HealthDataType.BloodOxygen: return(new GoogleFitData { TypeIdentifier = HealthDataTypes.TypeOxygenSaturation, AggregateType = HealthDataTypes.AggregateOxygenSaturationSummary, Unit = HealthFields.FieldOxygenSaturation, MinOverride = HealthFields.FieldOxygenSaturationMin, MaxOverride = HealthFields.FieldOxygenSaturationMax, AverageOverride = HealthFields.FieldOxygenSaturationAverage, }); case HealthDataType.BloodGlucose: return(new GoogleFitData { TypeIdentifier = HealthDataTypes.TypeBloodGlucose, AggregateType = HealthDataTypes.AggregateBloodGlucoseSummary, Unit = HealthFields.FieldBloodGlucoseLevel, }); // ANDROID SPECIFIC case HealthDataType.Droid_BasalMetabolicRate: return(new GoogleFitData { TypeIdentifier = DataType.TypeBasalMetabolicRate, AggregateType = DataType.AggregateBasalMetabolicRateSummary, Unit = Field.FieldCalories, }); default: throw new ArgumentOutOfRangeException(); } }