|
...
|
...
|
@@ -56,9 +56,7 @@ class ActivityBurnHeartRateZoneCard extends StatelessWidget { |
|
|
|
Text(
|
|
|
|
HealthReportSubjectScope.titleOf(
|
|
|
|
context,
|
|
|
|
summary.hasData
|
|
|
|
? context.l10n.activityHeartRateZone
|
|
|
|
: context.l10n.activityRealtimeHeartRate,
|
|
|
|
context.l10n.activityHeartRateZone,
|
|
|
|
),
|
|
|
|
style: TextStyle(
|
|
|
|
color: _h1,
|
|
...
|
...
|
@@ -284,11 +282,13 @@ class ActivityBurnHeartRateZoneCard extends StatelessWidget { |
|
|
|
}
|
|
|
|
|
|
|
|
DateTime get _startTime {
|
|
|
|
if (summary.startTime != null) return summary.startTime!;
|
|
|
|
if (summary.points.isNotEmpty) return summary.points.first.time;
|
|
|
|
if (summary.startTime != null) return _dayStart(summary.startTime!);
|
|
|
|
if (summary.points.isNotEmpty) return _dayStart(summary.points.first.time);
|
|
|
|
return DateTime(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
DateTime _dayStart(DateTime time) => DateTime(time.year, time.month, time.day);
|
|
|
|
|
|
|
|
List<ActivityBurnHeartRatePoint> _points(DateTime start, DateTime end) {
|
|
|
|
return summary.points
|
|
|
|
.where(
|
|
...
|
...
|
@@ -463,27 +463,26 @@ class _HeartRateAxis { |
|
|
|
ActivityBurnHeartRateSummary summary,
|
|
|
|
DateTime start,
|
|
|
|
) {
|
|
|
|
final maxMinutes = _dynamicMaxMinutes(start, DateTime.now());
|
|
|
|
final dayEnd = start.add(const Duration(days: 1));
|
|
|
|
final points = summary.points
|
|
|
|
.where(
|
|
|
|
(point) =>
|
|
|
|
point.bpm > 0 &&
|
|
|
|
!point.time.isBefore(start) &&
|
|
|
|
point.time.isBefore(dayEnd),
|
|
|
|
point.time.isBefore(dayEnd) &&
|
|
|
|
point.time.difference(start).inMinutes <= maxMinutes,
|
|
|
|
)
|
|
|
|
.toList();
|
|
|
|
if (points.isEmpty) {
|
|
|
|
return _HeartRateAxis(
|
|
|
|
endTime: start.add(const Duration(hours: 18)),
|
|
|
|
maxX: 1080,
|
|
|
|
endTime: start.add(Duration(minutes: maxMinutes)),
|
|
|
|
maxX: maxMinutes.toDouble(),
|
|
|
|
minY: 40,
|
|
|
|
maxY: 200,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
final coveredEnd = points.last.time;
|
|
|
|
final coveredMinutes = coveredEnd.difference(start).inMinutes;
|
|
|
|
final maxMinutes = _nextSixHourTick(coveredMinutes);
|
|
|
|
final heartRates = points.map((point) => point.bpm);
|
|
|
|
final minY = heartRates.reduce(math.min);
|
|
|
|
var maxY = _roundUpToFiveOrTen(heartRates.reduce(math.max));
|
|
...
|
...
|
@@ -506,11 +505,21 @@ class _HeartRateAxis { |
|
|
|
return value.toStringAsFixed(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _dynamicMaxMinutes(DateTime start, DateTime now) {
|
|
|
|
final today = DateTime(now.year, now.month, now.day);
|
|
|
|
if (start.isBefore(today)) return 1440;
|
|
|
|
if (start.isAfter(today)) return 360;
|
|
|
|
|
|
|
|
final coveredMinutes = now.difference(start).inMinutes;
|
|
|
|
return _nextSixHourTick(coveredMinutes);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _nextSixHourTick(int coveredMinutes) {
|
|
|
|
const tickMinutes = 360;
|
|
|
|
final clamped = coveredMinutes.clamp(0, 1440);
|
|
|
|
if (clamped == 0) return tickMinutes;
|
|
|
|
return (clamped ~/ tickMinutes + 1).clamp(1, 4) * tickMinutes;
|
|
|
|
final tickIndex = (clamped / tickMinutes).ceil().clamp(1, 4).toInt();
|
|
|
|
return tickIndex * tickMinutes;
|
|
|
|
}
|
|
|
|
|
|
|
|
static double _roundUpToFiveOrTen(double value) => (value / 5).ceil() * 5.0;
|
...
|
...
|
|