Skip to content

Commit

Permalink
added Movesense HR measures to the HR dataviz card + removed support …
Browse files Browse the repository at this point in the history
…for skinContact
  • Loading branch information
bardram committed Feb 20, 2024
1 parent 16e571d commit 1dc185c
Show file tree
Hide file tree
Showing 11 changed files with 41 additions and 57 deletions.
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import 'dart:convert';
import 'dart:ui' as ui;
import 'dart:io';

import 'package:async/async.dart';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
Expand Down
19 changes: 6 additions & 13 deletions lib/ui/cards/heart_rate_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ class HeartRateCardWidgetState extends State<HeartRateCardWidget>
child: Column(
children: <Widget>[
StreamBuilder(
stream: widget.model.heartRateEvents,
builder: (context, AsyncSnapshot<Measurement> snapshot) {
stream: widget.model.heartRateStream,
builder: (context, AsyncSnapshot<double> snapshot) {
// animationController.duration = Duration(
// milliseconds: 1000 ~/
// (((widget.model.currentHeartRate ?? 0) + 1) / 60));
Expand Down Expand Up @@ -155,9 +155,7 @@ class HeartRateCardWidgetState extends State<HeartRateCardWidget>
children: [
Container(
margin: const EdgeInsets.only(left: 8),
child: currentHeartRate != null ||
(!widget.model.contactStatus &&
currentHeartRate != null)
child: currentHeartRate != null
? Text(
currentHeartRate.toStringAsFixed(0),
style: heartRateTextStyle,
Expand All @@ -172,15 +170,10 @@ class HeartRateCardWidgetState extends State<HeartRateCardWidget>
children: [
RepaintBoundary(
child: ScaleTransition(
// scale should be _animation if the isOnWrist is true otherwise it should be no scale
scale: widget.model.contactStatus
? Tween<double>(begin: 1, end: 1)
.animate(animationController)
: animation,
scale: Tween<double>(begin: 1, end: 1)
.animate(animationController),
child: Icon(
widget.model.contactStatus
? Icons.favorite_outline_rounded
: Icons.favorite_rounded,
Icons.favorite_outline_rounded,
color: HeartRateCardWidget.colors[0],
size: 32,
),
Expand Down
2 changes: 1 addition & 1 deletion lib/view_models/cards/activity_data_model.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
part of '../../main.dart';
part of carp_study_app;

class ActivityCardViewModel extends SerializableViewModel<WeeklyActivities> {
Measurement _lastActivity =
Expand Down
57 changes: 23 additions & 34 deletions lib/view_models/cards/heart_rate_data_model.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
part of '../../main.dart';
part of carp_study_app;

class HeartRateCardViewModel extends SerializableViewModel<HourlyHeartRate> {
@override
Expand All @@ -11,52 +11,41 @@ class HeartRateCardViewModel extends SerializableViewModel<HourlyHeartRate> {
/// The current heart rate
double? get currentHeartRate => model.currentHeartRate;

/// Is the device touching skin?
///
/// Returns true if the device is touching skin, false otherwise. If the device
/// is not capable of detecting skin contact, this value is always true.
bool contactStatus = false;

HeartRateMinMaxPrHour get dayMinMax =>
HeartRateMinMaxPrHour(model.minHeartRate, model.maxHeartRate);

/// Stream of heart rate [PolarHR] measures.
Stream<Measurement>? get heartRateEvents => controller?.measurements
.where((measurement) => measurement.data is PolarHR);
final StreamGroup<double> _group = StreamGroup.broadcast();

Stream<double>? get heartRateStream => _group.stream.asBroadcastStream();

/// Stream of heart rate based on [PolarHR] measures.
Stream<double>? get polarHRStream => controller?.measurements
.where((measurement) => measurement.data is PolarHR)
.map((measurement) =>
(measurement.data as PolarHR).samples.firstOrNull?.hr.toDouble() ??
0);

/// Stream of heart rate based on [MovesenseHR] measures.
Stream<double>? get movesenseHRStream => controller?.measurements
.where((measurement) => measurement.data is MovesenseHR)
.map((measurement) => (measurement.data as MovesenseHR).hr);

@override
void init(SmartphoneDeploymentController ctrl) {
super.init(ctrl);

heartRateEvents?.listen(
(heartRateDataPoint) {
PolarHRSample heartRate =
(heartRateDataPoint.data as PolarHR).samples.firstOrNull ??
PolarHRSample(
hr: 0,
contactStatus: false,
rrsMs: [],
contactStatusSupported: true,
);

double hr = heartRate.hr.toDouble();
if (polarHRStream != null) _group.add(polarHRStream!);
if (movesenseHRStream != null) _group.add(movesenseHRStream!);

heartRateStream?.listen(
(hr) {
if (!(hr > 0)) {
contactStatus = false;
model.currentHeartRate = null;
return;
}
model.addHeartRate(DateTime.now().hour, hr);

if (hr > (model.maxHeartRate ?? 0)) {
model.maxHeartRate = hr;
}
if (hr < (model.minHeartRate ?? 100000)) {
model.minHeartRate = hr;
}

contactStatus =
heartRate.contactStatusSupported ? heartRate.contactStatus : true;

if (hr > (model.maxHeartRate ?? 0)) model.maxHeartRate = hr;
if (hr < (model.minHeartRate ?? 100000)) model.minHeartRate = hr;
model.resetDataAtMidnight();
},
);
Expand Down
2 changes: 1 addition & 1 deletion lib/view_models/cards/measurements_data_model.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
part of '../../main.dart';
part of carp_study_app;

class MeasurementsCardViewModel extends ViewModel {
final Map<String, int> _samplingTable = {};
Expand Down
2 changes: 1 addition & 1 deletion lib/view_models/cards/mobility_data_model.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
part of '../../main.dart';
part of carp_study_app;

class MobilityCardViewModel extends SerializableViewModel<WeeklyMobility> {
@override
Expand Down
2 changes: 1 addition & 1 deletion lib/view_models/cards/steps_data_model.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
part of '../../main.dart';
part of carp_study_app;

class StepsCardViewModel extends SerializableViewModel<WeeklySteps> {
StepCount? _lastStep;
Expand Down
2 changes: 1 addition & 1 deletion lib/view_models/cards/study_progress_data_model.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
part of '../../main.dart';
part of carp_study_app;

/// View model for the [StudyProgressCardWidget].
class StudyProgressCardViewModel extends ViewModel {
Expand Down
2 changes: 1 addition & 1 deletion lib/view_models/cards/task_data_model.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
part of '../../main.dart';
part of carp_study_app;

/// The data model for the the different task card widgets, like TaskCardWidget]
/// and [MediaCardWidget].
Expand Down
4 changes: 2 additions & 2 deletions test/heart_rate_data_model_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void main() {
viewModel.dayMinMax, equals(HeartRateMinMaxPrHour(null, null)));
expect(viewModel.hourlyHeartRate,
equals((HourlyHeartRate()).hourlyHeartRate));
expect(viewModel.contactStatus, equals(false));
// expect(viewModel.skinContact, equals(false));
});
test('with contactStatus being true', () async {
// Add a heart rate data point to the stream
Expand All @@ -115,7 +115,7 @@ void main() {
expect(viewModel.currentHeartRate, equals(anything));
expect(viewModel.dayMinMax, equals(anything));
expect(viewModel.hourlyHeartRate, equals(anything));
expect(viewModel.contactStatus, equals(true));
// expect(viewModel.skinContact, equals(true));
});
});
});
Expand Down
4 changes: 2 additions & 2 deletions test/heart_rate_data_model_test.mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -468,14 +468,14 @@ class MockSmartphoneDeploymentController extends _i1.Mock
class MockHeartRateCardViewModel extends _i1.Mock
implements _i4.HeartRateCardViewModel {
@override
bool get contactStatus => (super.noSuchMethod(
bool get skinContact => (super.noSuchMethod(
Invocation.getter(#contactStatus),
returnValue: false,
returnValueForMissingStub: false,
) as bool);

@override
set contactStatus(bool? _contactStatus) => super.noSuchMethod(
set skinContact(bool? _contactStatus) => super.noSuchMethod(
Invocation.setter(
#contactStatus,
_contactStatus,
Expand Down

0 comments on commit 1dc185c

Please sign in to comment.