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();
            }
        }
Beispiel #2
0
 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);
        }
Beispiel #4
0
 public abstract bool IsDataTypeAvailable(HealthDataType healthDataType);
Beispiel #5
0
        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;
Beispiel #8
0
 public abstract Task <bool> WriteAsync(HealthDataType healthDataType, double value, DateTime start, DateTime?end);
Beispiel #9
0
        //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();
            }
        }
Beispiel #10
0
        //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();
            }
        }