health_local_datasource.dart 9.97 KB
import 'package:doublefeel_flutter/core/error/app_error.dart';
import 'package:doublefeel_flutter/core/result/app_result.dart';
import 'package:doublefeel_flutter/core/services/health_raw_data_core_service.dart';
import 'package:doublefeel_flutter/data/models/enums/app_enums.dart';
import 'package:doublefeel_flutter/data/models/health/activity/activity_burn_statistics_data_v2.dart';
import 'package:doublefeel_flutter/data/models/health/health_v2_models.dart';
import 'package:doublefeel_flutter/data/models/health/hrv/hrv_statistics_data.dart';
import 'package:doublefeel_flutter/data/models/health/sleep/sleep_statistics_data.dart';

import 'health_datasource.dart';
import 'health_local_data_convert.dart';

class LocalHealthDataSource implements HealthDataSource {
  const LocalHealthDataSource({required this.coreService});

  final HealthRawDataCoreService coreService;

  @override
  Future<AppResult<SleepStatisticsData>> getSleepStatistics(
    int dateRangeType,
    int startDate, {
    int? queryUserId,
  }) async {
    try {
      final days = LocalHealthDataConvert.rangeDays(dateRangeType, startDate);
      if (days.isEmpty) return AppSuccess(SleepStatisticsData());

      final previousDays = LocalHealthDataConvert.previousRangeDays(
        dateRangeType,
        startDate,
      );
      final allDays = [...previousDays, ...days];
      final queryStart = LocalHealthDataConvert.unixSeconds(
        allDays.first.subtract(const Duration(days: 1)),
      );
      final queryEnd = LocalHealthDataConvert.unixSeconds(
        allDays.last.add(const Duration(days: 1)),
      );
      final sleepIntervals = await coreService.queryRawSleepIntervals(
        startTime: queryStart,
        endTime: queryEnd,
      );
      final heartRate = await coreService.queryRawDataPoints(
        dataType: HealthDataUploadType.heartRate.type,
        startTime: queryStart,
        endTime: queryEnd,
      );

      return AppSuccess(
        LocalHealthDataConvert.sleepStatistics(
          dateRangeType: dateRangeType,
          days: days,
          previousDays: previousDays,
          sleepIntervals: sleepIntervals,
          heartRate: heartRate,
        ),
      );
    } catch (error) {
      return AppFailure(AppUnknownError(error));
    }
  }

  @override
  Future<AppResult<ActivityBurnStatisticsDataV2>> getActivityBurnStatistics(
    int dateRangeType,
    int startDate, {
    int? queryUserId,
  }) async {
    try {
      final days = LocalHealthDataConvert.rangeDays(dateRangeType, startDate);
      if (days.isEmpty) return AppSuccess(ActivityBurnStatisticsDataV2());

      final previousDays = LocalHealthDataConvert.previousRangeDays(
        dateRangeType,
        startDate,
      );
      final allDays = [...previousDays, ...days];
      final queryStart = LocalHealthDataConvert.unixSeconds(allDays.first);
      final queryEnd = LocalHealthDataConvert.unixSeconds(
        days.last.add(const Duration(days: 1)),
      );
      final activity = await coreService.queryRawActivitySummaries(
        startTime: queryStart,
        endTime: queryEnd,
      );
      final heartRate = await coreService.queryRawDataPoints(
        dataType: HealthDataUploadType.heartRate.type,
        startTime: queryStart,
        endTime: queryEnd,
      );

      return AppSuccess(
        LocalHealthDataConvert.activityBurnStatistics(
          days: days,
          previousDays: previousDays,
          activity: activity,
          heartRate: heartRate,
          sleepIntervals: [],
        ),
      );
    } catch (error) {
      return AppFailure(AppUnknownError(error));
    }
  }

  @override
  Future<AppResult<HrvStatisticsDataV2>> getHrvStatistics(
    int dateRangeType,
    int startDate, {
    int? queryUserId,
  }) async {
    try {
      final days = LocalHealthDataConvert.rangeDays(dateRangeType, startDate);
      if (days.isEmpty) return AppSuccess(HrvStatisticsDataV2());

      final previousDays = LocalHealthDataConvert.previousRangeDays(
        dateRangeType,
        startDate,
      );
      final allDays = [...previousDays, ...days];
      final queryStart =
          LocalHealthDataConvert.unixSeconds(allDays.first) - 12 * 60;
      final queryEnd = LocalHealthDataConvert.unixSeconds(
            days.last.add(const Duration(days: 1)),
          ) +
          12 * 60;
      final hrvPoints = await coreService.queryHrvStressPoints(
        startTime: LocalHealthDataConvert.unixSeconds(days.first),
        endTime: LocalHealthDataConvert.unixSeconds(
          days.last.add(const Duration(days: 1)),
        ),
      );
      final realtimePoints = await coreService.queryRealtimeStressPoints(
        startTime: queryStart,
        endTime: queryEnd,
      );

      return AppSuccess(
        LocalHealthDataConvert.hrvStatistics(
          dateRangeType: dateRangeType,
          days: days,
          previousDays: previousDays,
          hrvPoints: hrvPoints,
          realtimePoints: realtimePoints,
        ),
      );
    } catch (error) {
      return AppFailure(AppUnknownError(error));
    }
  }

  @override
  Future<AppResult<V2HealthEverUploaded>> getHealthDataEverUploaded() async {
    try {
      final hasHealthData = await coreService.hasHealthData();
      return AppSuccess(V2HealthEverUploaded(flag: hasHealthData));
    } catch (error) {
      return AppFailure(AppUnknownError(error));
    }
  }

  @override
  Future<AppResult<V2ActivityTarget>> getV2ActivityTarget(
      int? queryUserId) async {
    try {
      final now = DateTime.now();
      final queryStart = LocalHealthDataConvert.unixSeconds(
        now.subtract(
            const Duration(days: HealthRawDataCoreService.defaultLookbackDays)),
      );
      final queryEnd = LocalHealthDataConvert.unixSeconds(now);
      final activity = await coreService.queryRawActivitySummaries(
        startTime: queryStart,
        endTime: queryEnd,
      );
      return AppSuccess(
        LocalHealthDataConvert.v2ActivityTarget(
          userId: 0,
          activity: activity,
        ),
      );
    } catch (error) {
      return AppFailure(AppUnknownError(error));
    }
  }

  @override
  Future<AppResult<V2HealthData>> getV2HealthData(
      int? queryUserId, int intDate) async {
    try {
      final day = LocalHealthDataConvert.dateFromKey(intDate);
      final dayStart = LocalHealthDataConvert.unixSeconds(day);
      final dayEnd = LocalHealthDataConvert.unixSeconds(
            day.add(const Duration(days: 1)),
          ) -
          1;
      final sleepStart = LocalHealthDataConvert.unixSeconds(
        day.subtract(const Duration(days: 1)),
      );
      final sleepEnd = LocalHealthDataConvert.unixSeconds(
        day.add(const Duration(days: 1)),
      );
      final hrvPoints = await coreService.queryHrvStressPoints(
        startTime: dayStart,
        endTime: dayEnd,
      );
      final activity = await coreService.queryRawActivitySummaries(
        startTime: dayStart,
        endTime: dayEnd,
      );
      final sleepingHeartRate = await coreService.queryRawDataPoints(
        dataType: HealthDataUploadType.heartRate.type,
        startTime: sleepStart,
        endTime: sleepEnd,
      );
      final restingHeartRate = await coreService.queryRawDataPoints(
        dataType: HealthDataUploadType.restingHeartRate.type,
        startTime: dayStart,
        endTime: dayEnd,
      );
      final sleepIntervals = await coreService.queryRawSleepIntervals(
        startTime: sleepStart,
        endTime: sleepEnd,
      );
      return AppSuccess(
        LocalHealthDataConvert.v2HealthData(
          day: day,
          hrvPoints: hrvPoints,
          activity: activity,
          sleepingHeartRate: sleepingHeartRate,
          restingHeartRate: restingHeartRate,
          sleepIntervals: sleepIntervals,
        ),
      );
    } catch (error) {
      return AppFailure(AppUnknownError(error));
    }
  }

  @override
  Future<AppResult<V2HrvTrendData>> getV2HrvTrend(
      int? queryUserId, int intDate) async {
    try {
      final (startTime, endTime) = _dayRange(intDate);
      final hrvPoints = await coreService.queryHrvStressPoints(
        startTime: startTime,
        endTime: endTime,
      );
      return AppSuccess(LocalHealthDataConvert.v2HrvTrendData(hrvPoints));
    } catch (error) {
      return AppFailure(AppUnknownError(error));
    }
  }

  @override
  Future<AppResult<V2LatestHrvData>> getV2LatestHrv(
      int? queryUserId, int intDate) async {
    try {
      final (startTime, endTime) = _dayRange(intDate);
      final hrvPoints = await coreService.queryHrvStressPoints(
        startTime: startTime,
        endTime: endTime,
      );
      return AppSuccess(LocalHealthDataConvert.v2LatestHrvData(hrvPoints));
    } catch (error) {
      return AppFailure(AppUnknownError(error));
    }
  }

  @override
  Future<AppResult<V2RealtimeStressData>> getV2RealtimeStress(
      int? queryUserId, int intDate) async {
    try {
      final (startTime, endTime) = _dayRange(intDate);
      final realtimePoints = await coreService.queryRealtimeStressPoints(
        startTime: startTime,
        endTime: endTime,
      );
      return AppSuccess(
        LocalHealthDataConvert.v2RealtimeStressData(realtimePoints),
      );
    } catch (error) {
      return AppFailure(AppUnknownError(error));
    }
  }

  @override
  Future<AppResult<V2StressScore>> getV2StressScore(
      int? queryUserId, int intDate) async {
    try {
      final (startTime, endTime) = _dayRange(intDate);
      final realtimePoints = await coreService.queryRealtimeStressPoints(
        startTime: startTime - 12 * 60,
        endTime: endTime + 12 * 60,
      );
      return AppSuccess(
        LocalHealthDataConvert.v2StressScore(
          realtimePoints,
          startTime: startTime,
          endTime: endTime,
        ),
      );
    } catch (error) {
      return AppFailure(AppUnknownError(error));
    }
  }

  (int startTime, int endTime) _dayRange(int intDate) {
    final day = LocalHealthDataConvert.dateFromKey(intDate);
    return (
      LocalHealthDataConvert.unixSeconds(day),
      LocalHealthDataConvert.unixSeconds(day.add(const Duration(days: 1))) - 1,
    );
  }
}